Repository navigation
Apply external class specs to user subclasses - #354
CatarinaGamboa wants to merge 3 commits into
Conversation
Fixes #353. A typestate call on a subclass of a specified class (e.g. a custom RuntimeException calling initCause) crashed with a Z3 sort mismatch. - RefinedVariable records supertypes transitively, so a subclass is related to the specified class however far up it is. - A constructor whose first statement is super(...) takes the state the super constructor's spec gives, with the super arguments renamed to this constructor's parameters. The lookup walks through Exception, RuntimeException and Error only, whose constructors delegate to Throwable's unchanged; other JDK classes may not (ClassNotFoundException(String) sets a null cause), so they are not assumed to. - RefinementProcessor registers all external specs before checking user classes, so the order of types in the package no longer matters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| return null; | ||
| } | ||
|
|
||
| private static boolean inheritSuperConstructorState(CtConstructor<?> c, RefinedFunction f, TypeChecker tc) { |
There was a problem hiding this comment.
this is a huge method can we maybe split it? and maybe add some documentation - at least 1 line what is doing
There was a problem hiding this comment.
Split in 2c1cfbc: inheritSuperConstructorState is now about 15 lines and calls superConstructorCall (find the leading super(...)), specifiedConstructor (find the spec, walking through the delegating JDK classes), superParamsToArguments (map super params to the argument names) and renameStates (substitute them into the to-states). Each one has a Javadoc line. The method's own Javadoc had also ended up above the DELEGATING_JDK_CLASSES constant. I moved it back onto the method.
- Log unresolvable supertypes in debug mode (new DebugLog.warn) - Walk supertypes in a single loop - Split inheritSuperConstructorState into documented helpers and fix its misplaced Javadoc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| * The given states with the super constructor's parameters renamed, or null if one of them mentions a parameter | ||
| * whose argument has no name. | ||
| */ | ||
| private static List<ObjectState> renameStates(List<Predicate> toStates, Map<String, String> rename) { |
There was a problem hiding this comment.
these are a lot of new functions in here, do we need them all? could we make them more concise? you can use more of a functional style if they end up more concise
There was a problem hiding this comment.
Cut down in 7aa1bb0: there are now 3 functions instead of 5, and the code is about 30 lines shorter.
superConstructorCallis inlined intoinheritSuperConstructorStateas a pattern-matching guard.superParamsToArgumentsandrenameStatesare merged into the main method. It builds a rename map and anunnamedset, rejects the inheritance if a state mentions an unnamed parameter (anyMatch), then maps each to-state through the substitutions with areduce.argumentNameis small, returns anOptional, and says how a single argument can be named.specifiedConstructortakes theCtExecutableReferencedirectly instead of its three parts.
I kept argumentName and specifiedConstructor separate: the first is the per-argument rule and the second is the JDK delegation walk, and inlining them would make the main method harder to read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixes #353.
Problem
An external spec (
@ExternalRefinementsFor("java.lang.Throwable")) did not reach user subclasses: callinginitCauseon a customRuntimeExceptionsubclass crashed withSort mismatch at argument #1 for function java.lang.Throwable.state1 … supplied sort is AppException.Example
With
super(message)instead, the sameinitCausepasses.Change
RefinedVariable.addSuperTypeswalks superclasses and interfaces transitively.AuxStateHandler: a constructor whose first statement issuper(...)inherits the to-state of the super constructor's spec, with the super arguments renamed to this constructor's parameters (or literals). If a to-state uses an argument that cannot be named, it falls back to the default state as before. The spec lookup walks up only throughException,RuntimeExceptionandError, whose constructors delegate unchanged toThrowable; other JDK subclasses are not assumed to (e.g.ClassNotFoundException(String)sets a null cause).RefinementProcessor: the first pass is split so all external specs are registered before any user class is checked; before, the result depended on the order of types in the package.Tests
testSuite/classes/throwable_subclass_error: subclass built withsuper(message, cause), theninitCause, gives a State Refinement Error.testSuite/classes/throwable_subclass_correct: subclass built withsuper(message), theninitCause, passes.mvn testpasses.🤖 Generated with Claude Code