Papers
Topics
Authors
Recent
Search
2000 character limit reached

Practical Range Refinement Types with Inference

Published 1 Jul 2026 in cs.PL | (2607.00824v1)

Abstract: Refinement types are a static verification technique that aims at increasing the expressivity of traditional type systems while remaining easy and natural to use. While systems based on refinement types have been developed for several mainstream languages, their practical adoption remains limited by their annotation overhead, which is often a more significant burden than when using the "plain" type annotations of languages like Java or Scala. To improve the state of the art, this paper introduces Ranger: a refinement type system designed to keep the annotation overhead small and to seamlessly integrate with imperative-style constructs like variables and loops. As the name suggests, Ranger focuses on integer range types: a particular kind of refinement types that express bounded integer ranges. Such types are widely useful to verify correct index manipulation and in-bounds data accesses, among others. To combine expressiveness and succinctness, Ranger is based on a bidirectional type system, which runs a type inference algorithm to provide the typechecking pass with information useful to reduce the need for user-written auxiliary annotations. Ranger also integrates other forms of lightweight flow-sensitive static analysis techniques that precisely capture the program's behavior without explicit annotations. We implemented Ranger on top of the Licorne experimental programming language. Our experiments show that Ranger's implementation can concisely express and verify a variety of useful properties that fall beyond the capabilities of standard static type systems like those of Java and Scala, and that Ranger compares favorably to other extended type systems, such as the Java Checker Framework and Liquid Java, that can also check properties about ranges.

Authors (2)

Summary

  • The paper’s main contribution is a bidirectional type inference that minimizes manual annotations for integer range refinements.
  • It combines flow-sensitive, monotonic analyses with SMT-backed constraint solving to ensure soundness and precise static verification.
  • The system enhances programmer usability by integrating these techniques in an SSA IR framework, achieving practical performance on benchmark programs.

Practical Range Refinement Types with Inference

Overview and Motivation

The paper "Practical Range Refinement Types with Inference" (2607.00824) presents a refinement type system focused on integer range types, targeting minimal annotation overhead and practical usability, especially in imperative-language settings. Refinement types allow static typing systems to express value-level properties (e.g., an integer being nonnegative or an array index being within bounds), significantly increasing the set of properties that can be verified without drastic increases in annotation effort or loss of ease of use typically found in mainstream language type systems.

The work's core contribution is a bidirectional type inference approach that significantly reduces the need for auxiliary code annotations. This solution is experimentally instantiated atop "Licorne," a research programming language, but the methodology is generic and protocol-agnostic, with potential adaptation to other imperative or OOP-focused languages.

Technical Contributions

Integer Range Types

The system introduces shorthand notations for interval-based integer types, supporting both closed <<l..u>><<l..u>> and half-open <<l..<u>><<l..<u>> ranges, as well as unbounded ends (e.g., <<0..>><<0..>> for nonnegative integers). Range types naturally encode invariants such as array bounds, counters, and similar idioms.

Bidirectional Typechecking and Inference

A central technical device is bidirectional typechecking, integrating top-down type propagation (from signatures) with bottom-up type analysis (from code). The algorithm precomputes candidate variable types through modular backward dataflow analysis. Candidate types inferred at loop entry, function returns, and function arguments are used as "hypotheses" during forward typechecking, with final verification ensuring soundness. Type inference is "aggressive" – all derived candidates must be validated during typechecking, thus precluding unsound conclusions.

Flow-sensitive and Monotonic Analyses

The type system is flow-sensitive, leveraging so-called "smart casts" akin to path-sensitive refinement. If a type predicate can be deduced to hold along a particular execution path (e.g., through a guard or a conditional expression), the system refines the variable type along that path. Loops are given special analysis through monotonicity detection, soundly recognizing variables that are monotonically (non-)increasing or constant across loop iterations. These facts permit strong invariants (e.g., an induction variable starting from zero and incremented only) and thus more precise range constraints with minimal or no user annotation.

Hybrid Cast Operator

When static typechecking cannot verify a required refinement, the system provides a hybrid cast operator {!!}, which statically enforces base type correctness and defers the refinement check to runtime. This mechanism is crucial for pragmatic usability in cases with unprovable or overly complex static constraints, emulating gradual typing.

Integration with Control-flow SSA IR

Typechecking operates not on the AST but on a static-single assignment (SSA) form, simplifying path- and flow-sensitive dataflow analyses. All variables are assigned once, and Ï•\phi nodes represent merged values at control-flow joins, making it easy to analyze monotonicity and back-propagation for variables in loops and branches.

Implementation and SMT-backed Constraint Solving

The prototype leverages Z3 for constraint discharge over quantifier-free linear integer formulas. Candidate types and inferred refinements are validated by SMT queries when implications between predicates or bounds must be checked, e.g., when proving subtyping between refinement types. The implementation operates efficiently by preprocessing constraints and solving only necessary path conditions, avoiding fixpoint iterations for candidate inference.

Empirical Evaluation

Expressiveness and Conciseness

Empirical evaluation benchmarks the proposed system against the Java Checker Framework and LiquidJava, using 14 reference programs annotated for each system. Results indicate that the proposed system:

  • Expresses all the needed integer constraints in the test suite, whereas the Checker Framework is less expressive (limited in supporting only fixed lower bounds such as −1,0,1-1, 0, 1, and less flexible range relationships).
  • Requires no auxiliary (in-body) annotations beyond those present in standard Scala code. By contrast, Java Checker Framework and LiquidJava often require extra in-body annotations and explicit casts to force invariants.
  • Offers higher annotation succinctness: The average number of annotations per constraint is lower compared to the Checker Framework (0.68 vs. 0.83), and matches or slightly exceeds LiquidJava (0.64). However, LiquidJava's soundness is compromised on examples with generics or certain advanced language features.

Soundness and Precision

  • The type system exhibits 100% soundness and precision in the benchmark set; all correct programs are accepted, all bugs inserted are detected, and no spurious errors or missed bugs are reported, provided the tool does not crash (LiquidJava crashed or silently accepted bugs on several examples).
  • The use of SMT solving enables reasoning about arithmetic equalities/inequalities and context-sensitive typing that was out of reach for previous tools (Checker Framework), such as expressing properties of lists produced by filters or constraints depending on multiple parameters.

Performance

Typechecking performance was practical: the system checked all 14 reference examples in 16 seconds, comparing favorably even to the Scala compiler, demonstrating that the combination of bidirectional inference, aggressive candidate propagation, and efficient SMT constraint discharge is suitable for real-world codebases at moderate scale.

Practical and Theoretical Implications

Programmer Usability

The research demonstrates that integrating range refinement types with strong inference and flow-sensitive analyses enables precise yet lightweight invariant checking. The annotation burden matches modern mainstream languages (e.g., Scala), and auxiliary annotations or explicit casts are required only in rare, statically unverifiable cases. This marks a distinct usability improvement over previous refinement type implementations in existing imperative/OOP languages.

Theoretical Impact

By proving that range refinements and backward/forward inference can be implemented efficiently atop an SSA IR, and that soundness can be maintained under best-effort candidate inference plus verification, the work suggests design patterns for integrating similar refinement mechanisms into future mainstream (e.g., Kotlin, Scala, Java) languages. The combination of hybrid typing, flow analysis, and SMT-backed validation formalizes a middle ground between classical typechecking and full dependent typing.

Future Developments

The bidirectional refinement type system exemplified here could serve as a foundation for extending to more general decidable refinement schemas—e.g., for user-defined predicates over algebraic data types, higher-order functions, or effects—without sacrificing succinctness or efficiency. Integration with language-level purity and effect systems (already present in this work for predicate purity) could be deepened, and the principles could be applied to retrofitting existing languages or informing new language features in type system design.

Conclusion

This work provides a comprehensive, practically viable methodology for integrating range refinement types and lightweight formal reasoning into a modern imperative programming environment. The core technical advances—aggressive bidirectional inference, flow/monotonicity-sensitive typing, and efficient SMT-based checking—yield a system with high expressivity, full soundness, minimal annotation burden, and practical performance. The study suggests that future mainstream language type systems can, and likely should, adopt similar approaches to increase static verification power without sacrificing usability or efficiency.

Paper to Video (Beta)

No one has generated a video about this paper yet.

Whiteboard

No one has generated a whiteboard explanation for this paper yet.

Open Problems

We haven't generated a list of open problems mentioned in this paper yet.

Tweets

Sign up for free to view the 1 tweet with 1 like about this paper.