feat(clone): reintroduce module overriding for stateless modules - #1104
Merged
Conversation
Module overriding in clones (`clone T with module X <- Y`, also `<=`, `=`, and `theory S <- S'` for theories containing modules) was removed by #458 as a fix for the unsoundness reported in #380: a clone transfers the statements of a theory without replaying their proofs, and proofs freely use the disjointness of program variables owned by distinct modules; identifying two modules that own state can therefore turn a proved statement into a false one. That argument does not apply to modules owning no program variable. The only facts the logic has about a concrete module are judgments on its procedures, its glob, the disjointness of its own variables, and memory restrictions (normalised to variable sets). With an empty own-glob and AST-equal components, the source and target modules are indiscernible, so substituting one for the other is a renaming. This restores the override for exactly that fragment. An override is accepted iff: - the source and the target own no program variable, transitively through their sub-modules (`EcCoreModules.module_own_state`; calling external stateful modules and taking module parameters is allowed); - the target is a concrete top-level module; - the components are AST-equal after the clone substitution (`EqTest.for_mexpr ~body:false`, so an alias target is accepted). Otherwise the clone fails at the override clause, naming the offending module: "declares state and cannot be overridden", "is not a concrete top-level module", or "is incompatible". `theory S <- S'` is likewise accepted when every module of `S` is stateless, and rejected naming the first stateful one (this replaces the former blanket rejection of any theory containing a module). Stateful modules keep failing as before. Mode semantics follow the pre-#458 code: `<-` rewrites references to the target and drops the item, `<=` additionally keeps an alias, `=` keeps an alias only. The alias mode now honours `rename [module]`. Both #380 counter-examples are kept as failing regression tests. Also fix a pre-existing defect in `EqTest.for_module_expr` reached by the new check: the two functor parameter lists were bound with `add_modules` (a `List.fold_left2`) without being compared, unlike `for_module_sig`. An arity mismatch raised `Invalid_argument`, and parameters of different module types were treated as equal whenever the body did not use them. Add the same `for_module_type` guard.
strub
force-pushed
the
clones-with-modules
branch
from
August 27, 2026 14:17
f85ba74 to
f94761f
Compare
strub
added a commit
that referenced
this pull request
Aug 27, 2026
Module overriding in clones (`clone T with module X <- Y`, also `<=`, `=`, and `theory S <- S'` for theories containing modules) was removed by #458 as a fix for the unsoundness reported in #380: a clone transfers the statements of a theory without replaying their proofs, and proofs freely use the disjointness of program variables owned by distinct modules; identifying two modules that own state can therefore turn a proved statement into a false one. That argument does not apply to modules owning no program variable. The only facts the logic has about a concrete module are judgments on its procedures, its glob, the disjointness of its own variables, and memory restrictions (normalised to variable sets). With an empty own-glob and AST-equal components, the source and target modules are indiscernible, so substituting one for the other is a renaming. This restores the override for exactly that fragment. An override is accepted iff: - the source and the target own no program variable, transitively through their sub-modules (`EcCoreModules.module_own_state`; calling external stateful modules and taking module parameters is allowed); - the target is a concrete top-level module; - the components are AST-equal after the clone substitution (`EqTest.for_mexpr ~body:false`, so an alias target is accepted). Otherwise the clone fails at the override clause, naming the offending module: "declares state and cannot be overridden", "is not a concrete top-level module", or "is incompatible". `theory S <- S'` is likewise accepted when every module of `S` is stateless, and rejected naming the first stateful one (this replaces the former blanket rejection of any theory containing a module). Stateful modules keep failing as before. Mode semantics follow the pre-#458 code: `<-` rewrites references to the target and drops the item, `<=` additionally keeps an alias, `=` keeps an alias only. The alias mode now honours `rename [module]`. Both #380 counter-examples are kept as failing regression tests. Also fix a pre-existing defect in `EqTest.for_module_expr` reached by the new check: the two functor parameter lists were bound with `add_modules` (a `List.fold_left2`) without being compared, unlike `for_module_sig`. An arity mismatch raised `Invalid_argument`, and parameters of different module types were treated as equal whenever the body did not use them. Add the same `for_module_type` guard. Cherry-picked from f94761f (clones-with-modules, PR #1104) onto a base predating 01fce12, with two adaptations: `add_moddef` still takes a `src:path` here, and `subst_mpath` re-rooted a `sb_moddef` hit with the *unsubstituted* functor arguments (`mp.m_args` instead of `args`), so a transferred lemma over a functor kept referring to the stale module ident of its refreshed binder (`forall (_ <: T), … G(A) …`). Fixed by using the substituted arguments, as the rewrite in 01fce12 does.
oskgo
approved these changes
Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Module overriding in clones (
clone T with module X <- Y, also<=,=, andtheory S <- S'for theories containing modules) was removed by #458 as a fix for the unsoundness reported in #380: a clone transfers the statements of a theory without replaying their proofs, and proofs freely use the disjointness of program variables owned by distinct modules; identifying two modules that own state can therefore turn a proved statement into a false one.That argument does not apply to modules owning no program variable. The only facts the logic has about a concrete module are judgments on its procedures, its glob, the disjointness of its own variables, and memory restrictions (normalised to variable sets). With an empty own-glob and AST-equal components, the source and target modules are indiscernible, so substituting one for the other is a renaming.
This restores the override for exactly that fragment. An override is accepted iff:
EcCoreModules.module_own_state; calling external stateful modules and taking module parameters is allowed);EqTest.for_mexpr ~body:false, so an alias target is accepted).Otherwise the clone fails at the override clause, naming the offending module: "declares state and cannot be overridden", "is not a concrete top-level module", or "is incompatible".
theory S <- S'is likewise accepted when every module ofSis stateless, and rejected naming the first stateful one (this replaces the former blanket rejection of any theory containing a module). Stateful modules keep failing as before.Mode semantics follow the pre-#458 code:
<-rewrites references to the target and drops the item,<=additionally keeps an alias,=keeps an alias only. The alias mode now honoursrename [module].Both #380 counter-examples are kept as failing regression tests (
tests/clone-module-stateless.ec).Also fix a pre-existing defect in
EqTest.for_module_exprreached by the new check: the two functor parameter lists were bound withadd_modules(aList.fold_left2) without being compared, unlikefor_module_sig. An arity mismatch raisedInvalid_argument, and parameters of different module types were treated as equal whenever the body did not use them. Add the samefor_module_typeguard.Related: #380 (left open — the stateful case is not addressed), #458 (the removal this partially reverts).