Mechanizing Refinement Types

Practical checkers based on refinement types use the combination of implicit semantic subtyping and parametric polymorphism to simplify the specification and automate the verification of sophisticated properties of programs. However, a formal metatheoretic accounting of the soundness of refinement type systems using this combination has proved elusive. We present λRF, a core refinement calculus that combines semantic subtyping and parametric polymorphism. We develop a metatheory for this calculus and prove soundness of the type system. Finally, we give two mechanizations of our metatheory. First, we introduce data propositions, a novel feature that enables encoding derivation trees for inductively defined judgments as refined data types, and use them to show that LiquidHaskell’s refinement types can be used for mechanization. Second, we mechanize our results in Coq, which comes with stronger soundness guarantees than LiquidHaskell, thereby laying the foundations for mechanizing the metatheory of LiquidHaskell.


INTRODUCTION
Refinements constrain types with logical predicates to specify new concepts.For example, the refinement type Pos Int{ : 0 < } describes positive integers and Nat Int{ : 0 ≤ } specifies natural numbers.Refinements on types have been successfully used to define sophisticated concepts (e.g.secrecy [Fournet et al. 2011], resource constraints [Knoth et al. 2020], security policies [Lehmann et al. 2021]) that can then be verified in programs developed in various programming languages like Haskell [Vazou et al. 2014b], Scala [Hamza et al. 2019], Racket [Kent et al. 2016] and Ruby [Kazerounian et al. 2017].
The success of refinement types relies on the combination of two essential features.First, implicit semantic subtyping uses semantic (SMT-solver based) reasoning to automatically convert the types of expressions without troubling the programmer for explicit type casts.For example, consider a positive expression : Pos and a function expecting natural numbers : Nat → Int.
To type check the application , the refinement type system will implicitly convert the type of from Pos to Nat, because 0 < ⇒ 0 ≤ semantically holds.Importantly, refinement types propagate semantic subtyping inside type constructors to, for example, treat function arguments in a contravariant manner.Second, parametric polymorphism allows the propagation of the refined types through polymorphic function interfaces, without the need for extra reasoning.As a trivial example, once we have established that is positive, parametric polymorphism should let us conclude that : Pos if, for example, is the identity function : → .As is often the case with useful ideas, the engineering of practical tools has galloped far ahead of the development of the meta-theoretical foundations for refinements with subtyping and polymorphism.In fact, such a development is difficult.As Sekiyama et al. [2017] observe, a naïve combination of type variables and subtyping leads to unsoundness because potentially contradicting refinements can be lost at type instantiation.Their suggested solution replaces semantic with syntactic subtyping, which is significantly less expressive.Other recent formalizations of refinement types either drop semantic subtyping [Hamza et al. 2019] or polymorphism [Flanagan 2006;Swamy et al. 2016].
In this paper we present , a core calculus with a refinement type system that combines semantic subtyping with refined polymorphic type variables.We develop and establish the properties of with four concrete contributions.
1. Reconciliation Our first contribution is a language that combines refinements and polymorphism in a way that ensures the metatheory remains sound without sacrificing the expressiveness needed for practical verification.To this end, introduces a kind system that distinguishes the type variables that can be soundly refined (without the risk of losing refinements at instantiation) from the rest, which are then left unrefined.In addition our design includes a form of existential typing [Knowles and Flanagan 2009b] which is essential to synthesize the types -in the sense of bidirectional typing -for applications and let-binders in a compositional manner ( § 3, 4).

Foundation
Our second contribution is to establish the foundations of by proving soundness, which says that if has a type then, either is a value or it can step to another term of the same type.The combination of semantic subtyping, polymorphism, and existentials makes the soundness proof challenging with circular dependencies that do not arise in standard (unrefined) calculi.To ease the presentation and tease out the essential ingredients of the proof we stage the metatheory.First, we review an unrefined base language , a classic System F [Pierce 2002] with primitive Int and Bool types ( § 5).Next, we show how refinements (kinds, subtyping, and existentials) must be accounted for to establish the soundness of ( § 6).
3. Reification Our third contribution is to introduce data propositions a novel feature that enables the encoding of derivation trees for inductively defined judgments as refined data types, by first reifying the propositions and evidence as plain Haskell data, and then using refinements to connect the two.Hence, data propositions let us write plain Haskell functions over refined data to provide explicit, constructive proofs ( § 7).Without data propositions reasoning about potentially non-terminating computations was not possible in L H , thereby precluding even simple meta-theoretic developments such as the soundness of let alone .
4. Verification Our final contribution is to fully mechanize the metatheory of using L H . Our formalization uses data propositions and recursive Haskell functions on derivation trees to produce explicit witnesses that correspond to proofs of our soundness theorems [Vazou et al. 2018].Our proof is non-trivial, requiring 9,400 lines of code and 30 minutes to verify.While we are certain that it is possible, and perhaps simpler, to implement the mechanization with specialized proof assistants and tactics, our contribution is to show this is feasible purely as a (refined) Haskell program.Indeed, we show that substantial meta-theoretical formalizations over arbitrary computations are feasible via data propositions via L H -style refinement typing ( § 8).

OVERVIEW
Our overall strategy is to present the metatheory for in two parts.First, we review the metatheory for : a familiar starting point that corresponds to the full language with refinements erased ( § 5).Second, we use the scaffolding established by to highlight the extensions needed to develop the metatheory for refinements in ( § 6).Let's begin with a high-level overview that describes a proof skeleton that is shared across the developments for and , the specific challenges posed by refinements, and the machinery needed to go from the simpler to the refined .
Types and Terms Both and have the same syntax for terms (Fig. 2).has the usual syntax for types familiar from System F, while additionally allows ( 's) types to be refined by terms (respectively, the white parts and all of Fig. 3), and existential types.Both languages include a notion of kinds that qualify the types that are allowed to be refined.
Judgments Both languages have typing judgments Γ ⊢ : which say that a term has type with respect to a binding environment (i.e.context) Γ.Additionally, both languages have well-formedness judgments Γ ⊢ : which say that a type has the kind in context Γ, by requiring that the free variables in are appropriately bound in the environment Γ. (Though some presentations of [Pierce 2002] eschew well-formedness judgments, they are helpful for a mechanized metatheory [Aydemir et al. 2008]).Crucially, has a subtyping judgment Γ ⊢ 1 2 which says that type 1 is a subtype of 2 in context Γ. Subtyping for refined base types is established via an axiomatized implication judgment Γ ⊢ ⇒ which says that the term logically implies the term whenever their free variables are given values described by Γ.We take an axiomatized approach to capture the properties required by an implication checking oracle for proving soundness.
Proof Landscape Fig. 1 charts the overall landscape of our formal development as a dependency graph of the main lemmas which establish meta-theoretic properties of the different judgments.
Nodes shaded light grey represent lemmas in the metatheories for and .The dark grey nodes denote lemmas that only appear in .An arrow shows a dependency: the lemma at the tail is used in the proof of the lemma at the head.Darker arrows are dependencies in only.

Soundness via Preservation and Progress
For both and we establish soundness via • Progress: If a closed term is well-typed, then either it is a value or it can be further evaluated; • Preservation: If a closed term is well-typed, then its type is preserved under evaluation.
The type soundness theorem states that a well-typed closed term cannot become stuck: any sequence of evaluation steps will either end with a value or the sequence can be extended by another step.Next, we describe the lemmas used to establish preservation and progress for and then outline the essential new ingredients that demonstrate soundness for the refined .

Metatheory for
Progress in is standard as the typing rules are syntax-directed.The top-level rule used to obtain the typing derivation for a term uniquely determines the syntactic structure of which lets us use the appropriate small-step reduction rule to obtain the next step of the evaluation of .
Preservation says that when a well-typed expression steps to ′ , then ′ is also well-typed.
As usual, the non-trivial case is when the step is a type abstraction Λ : .(respectively lambda abstraction .) applied to a type (respectively value), in which case the term ′ is obtained by substituting the type or value appropriately in .Thus, our metatheory requires us to prove a Substitution Lemma, which describes how typing judgments behave under substitution of free term or type variables.Additionally, some of our typing rules use well-formedness judgments and so we must also prove that well-formedness is preserved by substitution.
Substitution requires some technical lemmas that let us weaken judgments by adding any fresh variable to the binding environment.
Primitives Finally, the primitive reduction steps (e.g.arithmetic operations) require the assumption that the reduction rules defined for the built-in primitives are type preserving.

What's hard about Refinements?
Subtyping Refinement types rely on implicit semantic subtyping, that is, type conversion (from subtypes) happens without any explicit casts and is checked semantically via logical validity.For example, consider a function that requires natural numbers as input, applied to a positive argument .Let Γ : Nat → Int, : Pos The application will type check as below, using the T S rule to implicitly convert the type of the argument and the S B rule to check that positive integers are always naturals by checking the validity of the formula ∀ .0 < ⇒ 0 ≤ .
Importantly, most refinement type systems use type-constructor directed rules to destruct subtyping obligations into basic (semantic) implications.For example, in Fig. 8 the rule S F states that functions are covariant on the result and contravariant on the arguments.Thus, a refinement type system can, without any annotations or casts, decide that : Nat → Pos is a suitable argument for the higher order function : (Pos → Nat) → Int.
Existentials For compositional and decidable type checking, some refinement type systems use an existential type [Knowles and Flanagan 2009a] to check dependent function application, i.e. the TA E rule below, instead of the standard type-theoretic TA E rule.
To understand the difference, consider some expression of type Pos and the identity function The application is typed as Int{ : = } with the TA E rule, which has two problems.First, the information that is positive is lost.To regain this information the system needs to re-analyze the expression breaking compositional reasoning.Second, the arbitrary expression enters the refinement logic making it impossible for the system to restrict refinements into decidable logical fragments.Using the TA E rule, both of these problems are addressed.The type of becomes ∃ :Pos.Int{ : = } preserving the information that the application argument is positive, while the variable cannot break any carefully crafted decidability guarantees.Knowles and Flanagan [2009a] introduce the existential application rule and show that it preserves the decidability and completeness of the refinement type system.An alternative approach for decidable and compositional type checking is to ensure that all the application arguments are variables by ANF transforming the original program [Flanagan et al. 1993].ANF is more amicable to implementation as it does not require the definition of one more type form.However, ANF is more problematic for the metatheory, as ANF is not preserved by evaluation.Additionally, existentials let us synthesize types for let-binders in a bidirectional style: when typing let = 1 in 2 , the existential lets us eliminate from the type synthesized for 2 , yielding a precise, algorithmic system [Cosman and Jhala 2017].Thus, we choose to use existential types in .
Polymorphism Polymorphism is a precious type abstraction [Wadler 1989], but combined with refinements, it can lead to imprecise or, worse, unsound systems.As an example, below we present the function max with four potential type signatures.
As a first attempt, we give max a monomorphic type, stating that the result of max is an integer greater or equal to any of its arguments.This type is insufficient because it forgets any information known for max's arguments.For example, if both arguments are positive, the system cannot decide that max x y is also positive.To preserve the argument information we give max a polymorphic type, as a second attempt.Now the system can deduce that max x y is positive, but forgets that it is also greater or equal to both x and y.In a third attempt, we naively combine the benefits of polymorphism with refinements to give max a very precise type that is sufficient to propagate the arguments' properties (positivity) and max behavior (inequality).
Unfortunately, refinements on arbitrary type variables are dangerous for two reasons.First, the type of max implies that the system allows comparison between any values (including functions).Second, if refinements on type variables are allowed, then, for soundness [Belo et al. 2011], all the types that substitute variables should be refined.For example, if a type variable is refined with false (that is, { : false}) and gets instantiated with an unrefined function type ( : → ), then the false refinement is lost and the system becomes unsound.
Base Kind when Refined To preserve the benefits on refinements on type variables, without the complications of refining function types, we introduce a kind system that separates the type variables that can be refined with the ones that cannot.Variables with the base kind , can be refined, compared, and only substituted by base, refined types.The other type variables have kind ★ and can only be trivially refined with true.With this kind system, we give max a polymorphic and precise type that naturally rejects non comparable (e.g.function) arguments.

From to
The metatheory for requires us to enrich that of with three essential and non-trivial blocks -shown as shaded regions in Fig. 1 -that help surmount the challenges posed by the combination of refinements with existentials, subtyping and polymorphism.
Typing Inversion First, thanks to (refinement) subtyping is not syntax directed, and so we cannot directly invert the typing derivations of terms to get derivations for their sub-terms.For example, we cannot directly invert a derivation Γ ⊢ .: : → to obtain a typing derivation that the body has type because the above derivation may have been established using (multiple instances of) subtyping.The typing inversion lemmas addresses this problem by using the transitivity of subtyping to restructure the judgment tree to collapse all use of subtyping in a way that lets us invert the non-subtyping judgment to conclude that if a term (e.g. .) is well-typed, then its components (e.g. ) are also well-typed.The proof of transitivity of subtyping is non-trivial due to the presence of existential types.We cannot proceed by induction on the structure of the two subtyping judgments (Γ ⊢ 1 2 and Γ ⊢ 2 3 ), because we do not apply the inductive hypothesis directly to subderivations.We must first apply the substitution and narrowing lemmas in various cases, which may increase the size of derivations used in the inductive hypothesis ( § 6.1).Instead, our proof goes by induction on a more intricate size (the combined depth of types 1 , 2 , and 3 ).
Subtyping The biggest difference between the two metatheories is that has a notion of subtyping which is crucial to making refinements practical.Subtyping complicates by introducing a mutual dependency between the lemmas for typing and subtyping judgments.Recall that typing depends on subtyping due to the usual subsumption rule (T S in Fig. 7) that lets us weaken the type of a term with a super-type.Conversely, subtyping depends upon typing because of the rule (S W in Fig. 8) which establishes subtyping between existential types.Thanks to this mutual dependency, all of the lemmas from that relate to typing judgments, i.e. the weakening and substitution lemmas, are now mutually recursive with new versions for subtyping judgments shown in the diagonal lined region in Fig. 1.
Narrowing Finally, due to subtyping, the proofs of the typing inversion and substitution lemmas for require narrowing lemmas that allow us to replace a type that appears inside the binding environment of a judgment with a subtype, thus "narrowing" the scope of the judgment.Due to the mutual dependencies between the typing and subtyping judgments of , we must prove narrowing for both typing and subtyping, which in turn depend on narrowing for well-formedness judgments.A few important cases of the narrowing proofs require other technical lemmas shown in the checkerboard region of Fig. 1.For example, the proof of narrowing for the "occurrence-typing" rule T V that crucially enables path-sensitive reasoning, uses a lemma on selfifying [Ou et al. 2004] the types involved in the judgments.

LANGUAGE
For brevity, clarity and also to cut a circularity in the metatheory (in rule WF R in § 4.1), we formalize refinements using two calculi.The first is the base language : a classic System F [Pierce 2002] with call-by-value semantics extended with primitive Int and Bool types and operations.The second calculus is the refined language which extends with refinements.By using the first calculus to express the typing judgments for our refinements, we avoid making the wellformedness and typing judgments be mutually dependent in our full language.We use the grey highlights to indicate the extensions to the syntax and rules of needed to support refinements in .

Syntax
We start by describing the syntax of terms and types in the two calculi.
Constants, Values and Terms Fig. 2 summarizes the syntax of terms in both calculi.Terms are stratified into primitive constants and values.The primitives include Int and Bool constants, primitive boolean operations, and polymorphic comparison and equality primitive.Values are those expressions which cannot be evaluated any further, including primitive constants, binders and -and type-abstractions.Finally, the terms comprise values, value-and type-applications, let-binders and annotated expressions.
Kinds & Types Fig. 3 shows the syntax of the types, with the grey boxes indicating the extensions to required by .In , only base types Bool and Int can be refined: we do not permit refinements for functions and polymorphic types.enforces this restriction using two kinds which denote types that may ( ) or may not (★) be refined.The (unrefined) base types comprise Int, Bool, and type variables .The simplest type is of the form { : } comprising a base type and a refinement that restricts to the subset of values that satisfy i.e. for which evaluates to true.We use refined base types to build up dependent function types (where the input parameter can appear in the output type's refinement), existential and polymorphic types.In the sequel, we write to abbreviate { : true} and call types refined with only true "trivially refined" types.

Note on Variable Representation
Our metatheory requires that all variables bound in the environment be distinct.Our mechanization enforces this invariant via the locally nameless representation [Aydemir et al. 2005]: free and bound variables become distinct objects in the syntax, as are type and term variables.All free variables have unique names which never conflict with bound variables represented as de Bruijn indices.This eliminates the possibility of capture in substitution and the need to perform alpha-renaming during substitution.The locally nameless representation avoids technical manipulations such as index shifting by using names instead of indices for free variables (we discuss alternatives in § 9).To simplify the presentation of the syntax and rules, we use names for bound variables to make the dependent nature of the function arrow clear.

Dynamic Semantics
Fig. 4 summarizes the substitution-based, call-by-value, contextual, small-step semantics for both calculi.We specify the reduction semantics of the primitives using the functions and .

Substitution
The key difference with standard formulations is the notion of substitution for type variables at (polymorphic) type-application sites as shown in rule E A TA in Fig. 4. Fig. 5 summarizes how type substitution is defined, which is standard except for the last line which defines the substitution of a type variable in a refined type variable { : } with a type which is potentially refined.To do this substitution, we combine with the type by using strengthen( , , ) which essentially conjoins the refinement to the top-level refinement of a base-kinded .For existential types, strengthen pushes the refinement through the existential quantifier.Function and quantified types are left unchanged as they cannot be used to instantiate a refined type variable (which must be of base kind).
Primitives The function ( , ) specifies what an application of a built-in monomorphic primitive evaluates to.The reductions are defined in a curried manner, i.e. we have that ≤ ↩→ * ( (≤, ), ).Currying gives us unary relations like ≤ which is a partially evaluated version of the ≤ relation.We also denote by (=, ⌊ ⌋) and (≤, ⌊ ⌋) a function specifying the reduction rules for type applications for the polymorphic built-in primitives = and ≤.
Determinism Our proof of soundness uses the following property of the operational semantics.

L 3.1 (D ). For every expression ,
• there exists at most one term ′ such that ↩→ ′ , • there exists at most one value such that ↩→ * , and • if is a value there is no term ′ such that ↩→ ′ .

STATIC SEMANTICS
The static semantics of our calculi comprise four main judgment forms: well-formedness judgments that determine when a type or environment is syntactically well-formed (in and ); typing judgments that stipulate that a term has a particular type in a given context (in and ); subtyping judgments that establish when one type can be viewed as a subtype of another (in ); and implication judgments that establish when one predicate implies another (in ).Next, we present the static semantics of by describing each of these judgments and the rules used to establish them.We use grey to highlight the antecedents and rules specific to .
Co-finite Quantification We define our rules using the co-finite quantification technique of Aydemir et al. [2008].This technique enforces a small (but critical) restriction in the way fresh names are introduced in the antecedents of rules.For example, below we present the standard (on the left) and our (on the right) rules for type abstraction.
The standard rule T A E requires the existence of a fresh type variable name ′ .Instead our cofinite quantification rule states that the rule holds for any name excluding a finite set of names .
As observed by Aydemir et al. [2008] this rephrasing simplifies the mechanization of metatheory by eliminating the need for renaming lemmas.

Well-formedness
Judgments The ternary judgment Γ ⊢ : says that the type is well-formed in the environment Γ and has kind .The judgment ⊢ Γ says that the environment Γ is well formed, meaning that variables are only bound to well-formed types.Well-formedness is also used in the (unrefined) system , where Γ ⊢ : means that the (unrefined) type is well-formed in environment Γ and has kind and ⊢ Γ means that the free type and expression variables of the unrefined environment Γ are bound earlier in the environment.While well-formedness is not strictly required for , we found it helpful to simplify the mechanization [Rémy 2021].
Rules Fig. 6 summarizes the rules that establish the well-formedness of types and environments, with the grey highlighting the parts relevant for refinements.Rule WF B states that the two closed base types (Int and Bool) are well-formed and have base kind on their own or with trivial refinement true.Similarly, rule WF V says that an unrefined or trivially refined type variable is well-formed having kind so long as : is bound in the environment.The rule WF R stipulates that a refined base type { : } is well-formed with base kind in some environment if the unrefined base type has base kind in the same environment and if the refinement predicate has type Bool in the environment augmented by binding a fresh variable to type .Note that if ≡ then we can only form the antecedent Γ ⊢ { : true} : when : ∈ Γ (rule WF V ), which prevents us from refining star-kinded type variables.To break a circularity in our judgments, in which well-formedness judgments can appear in the antecedent position of typing judgments and a typing judgment would appear in the antecedent position of WF R , we stipulate only a judgment for having underlying type Bool.Our rule WF F states that a function type : → is well-formed with star kind in some environment Γ if both type is well-formed (with any kind) in the same environment and type is well-formed (with any kind) in the environment Γ augmented by binding a fresh variable to .Rule WF E states that an existential type ∃ : . is well-formed with some kind in some environment Γ if both type is well-formed (with any kind) in the same environment and type is well-formed with kind in the environment Γ augmented by binding a fresh variable to .Rule WF P establishes that a polymorphic type ∀ : .has star kind in environment Γ if the inner type is well-formed (with any kind) in environment Γ augmented by binding a fresh type variable to kind .Finally, rule WF K simply states that if a type is well-formed with base kind in some environment, then it is also well-formed with star kind.This rule is required by our metatheory to convert base to star kinds in type variables.
As for environments, rule WFE E states that the empty environment is well-formed.Rule WFE B says that a well-formed environment Γ remains well-formed after binding a fresh variable to any type that is well-formed in Γ. Finally rule WFE TB states that a well-formed environment remains well-formed after binding a fresh type variable to any kind.

Typing
The judgment Γ ⊢ : states that the term has type in the context of environment Γ.We write Γ ⊢ : to indicate that term has the (unrefined) type in the (unrefined) context Γ. Fig. 7 summarizes the rules that establish typing for both and , with grey for the extensions.
Well-formed Type Γ ⊢ : Fig. 6.Well-formedness of types and environments.The rules for exclude the grey boxes.
Typing Primitives The type of a built-in primitive is given by the function ty( ), which is defined for every constant of our system.Below we present essential examples of the ty( ) definition.

ty(true)
Bool{ : = true} ty(3) We note that the = used in the refinements is the polymorphic equals with type applications elided.Further, we use ≤ to represent an arbitrary member of the infinite family of primitives 0≤, 1≤ , 2≤, . ... For we erase the refinements using ⌊ty( )⌋.The rest of the definition is similar.
Our choice to make the typing and reduction of constants external to our language, i.e. respectively given by the functions ty( ) and ( ), makes our system easily extensible with further constants.The requirement, for soundness, is that these two functions on constants together satisfy the following four conditions.
T S Fig. 7. Typing rules.The judgment Γ ⊢ : is defined by excluding the grey boxes.
(2) If ty( ) = : → or ty( ) = ∀ : ., then ⊢ ty( ) : ★.To type constants, rule T P gives the type ( ) to any built-in primitive , in any context.The typing rules for boolean and integer constants are included in T P .
Typing Variables with Selfification Rule T V establishes that any variable that appears as : in environment Γ can be given the selfified type [Ou et al. 2004] self( , , ) provided that Γ ⊢ : .This rule is crucial in practice, to enable path-sensitive "occurrence" typing [Tobin-Hochstadt and Felleise 2008], where the types of variables are refined by control-flow guards.For example, suppose we want to establish : ⊢ ( . ) : : → { : = }, and not just : ⊢ ( . ) : → .The latter judgment would result from applying rule T A if T V merely stated that Γ ⊢ : whenever : ∈ Γ.Thus we need to strengthen the T V rule to be selfified.Informally, to get information about into the refinement level, we need to say that is constrained to elements of type that are equal to itself.In order to express the exact type of variables, we introduce a "selfification" function that strengthens a refinement with the condition that a value is equal to itself.Since abstractions do not admit equality, we only selfify the base types and the existential quantifications of them; using the self function defined below.
Typing Applications with Existentials Our rule T A states the conditions for typing a term application .Under the same environment, we must be able to type at some function type : → and at .Then we can give the existential type ∃ : . .The use of existential types in rule T A is one of the distinctive features of our language and was introduced by Knowles and Flanagan [2009b].As overviewed in § 2.2, we chose this form of T A over the , Vol. 1, No. 1, Article .Publication date: July 2022.conventional form with Γ ⊢ : [ / ] in the consequent position because our version prevents the substitution of arbitrary expressions (e.g.functions and type abstractions) into refinements.As an alternative, we could have used A-Normal Form [Flanagan et al. 1993], but since this form is not preserved under the small step operational semantics, it would greatly compicate our metatheory, by forcing the definition of closing subtitutions for non-value expressions.
Other Typing Rules Rule T A says that we can type a lambda abstraction .at a function type : → whenever is well-formed and the body can be typed at in the environment augmented by binding a fresh variable to .Our rule T TA states that whenever a term has polymorphic type ∀ : ., then for any well-formed type with kind in the same environment, we can give the type [ / ] to the type application [ ].For the variant of T TA , we erase the refinements (via ⌊ ⌋) before checking well-formedness and performing the substitution.The rule T TA establishes that a type-abstraction Λ : .can be given a polymorphic type ∀ : . in some environment Γ whenever can be given the well-formed type in the environment Γ augmented by binding a fresh type variable to kind .Next, rule T L states that an expression let = in has type in some environment whenever is well-formed, can be given some type , and the body can be given type in the augmented environment formed by binding a fresh variable to .Rule T A establishes that an explicit annotation : can indeed be given the type when the underlying expression has type and is well-formed in the same context.The version of the rule erases the refinements and uses ⌊ ⌋.Finally, rule T S tells us that we can exchange a subtype for a supertype in a judgment Γ ⊢ : provided that is well-formed in the same context and Γ ⊢ , which we present next.

Subtyping
Judgments Fig. 8 summarizes the rules that establish the subtyping judgment.The subtyping judgment Γ ⊢ stipulates that the type is a subtype of type the in the environment Γ and is used in the subsumption typing rule T S (of Fig. 7).

Subtyping Rules The rule S F
states that one function type 1 : 1 → 1 is a subtype of another function type 2 : 2 → 2 in a given environment Γ when both 2 is a subtype of 1 and 1 is a subtype of 2 when we augment Γ by binding a fresh variable to type 2 .As usual, note that function subtyping is contravariant in the input type and covariant in the outputs.Rules S B and S W establish subtyping for existential types [Knowles and Flanagan 2009b], respectively when the existential appears on the left or right.Rule S B allows us to exchange a universal quantifier (a variable bound to some type in the environment) for an existential quantifier.If we have a judgment of the form : ′ where does not appear free in either ′ or in the context Γ, then we can conclude that ∃ : . is a subtype of ′ .Rule S W states that if type is a subtype of ′ [ / ] for some value of type , then we can discard the specific witness for and quantify existentially to obtain that is a subtype of ∃ : .′ .Rule S P states when one polymorphic type ∀ : . 1 is a subtype of another polymorphic type ∀ : . 2 in some environment Γ.The requirement is that 1 be a subtype of 2 in the environment where we augment Γ by binding a fresh type variable to kind .
Refinements enter the scene in the rule S B which uses implication to specify that a refined base type { 1 : 1 } is a subtype of another { 2 : 2 } in context Γ when 1 implies 2 in the environment Γ augmented by binding a fresh variable to the unrefined type .Next, we describe how implication is formalized in our system.

Implication
The implication judgment Γ ⊢ 1 ⇒ 2 states that the implication 1 ⇒ 2 is (logically) valid under the assumptions captured by the context Γ.In refinement type implementations [Swamy et al. 2016;Vazou et al. 2014a], this relation is implemented as an external automated (usually SMT) solver.In non-mechanized refinement type formalizations, there have been two approaches to formalize predicate implication.Either directly reduce it into a logical implication (e.g. in Gordon and Fournet [2010]) or define it using operational semantics (e.g. in Vazou et al. [2018]).It turns out that none of these approaches can be directly encoded in a mechanized proof.The former approach is insufficient because it requires a formal connection between the (deeply embedded) terms of and the terms of the logic, which has not yet been clearly established.The second approach is more direct, since it gives meaning to implication using directly the terms of , via denotational semantics.Sadly, the definition of denotational semantics for our polymorphic calculus is not currently possible: encoding type denotations as an inductive data type (or proposition in our L H encoding § 8) requires a negative occurrence which is not currently admitted.

Abstracting over SMT-based Implication
To bypass these problems we follow the approach of Lehmann and Tanter [2016] and encode implication as an axiomatized judgment that satisfies the below requirements.

R
2. The implication relation satisfies the following statements: (1) (Reflexivity) Γ ⊢ ⇒ . ( This axiomatic approach precisely explicates the properties that are required of the implication checker in order to establish the soundness of the entire refinement type system.In the future, we can look into either verifying that these properties hold for SMT-based checkers, or even build other kinds of implication oracles that adhere to this contract.

5
SOUNDNESS Next, we present the metatheory of the underlying (unrefined) that, even though it follows the textbook techniques of Pierce [2002], it is a convenient stepping stone towards the metatheory for (refined) .In addition, the soundness results for are used for our full metatheory, as our wellformedness judgments require the refinement predicate to have the type Bool thereby avoiding the circularity of using a regular typing judgment in the antecedents of the well-formedness rules.The light grey boxes in Fig. 1 show the high level outline of the metatheory for which provides a miniaturized model for but without the challenges of subtyping and existentials.Next, we describe the top-level type safety result, how it is decomposed into progress ( § 5.1) and preservation ( § 5.2) lemmas, and the various technical results that support the lemmas.
The main type safety theorem for states that a well-typed term does not get stuck: i.e. either evaluates to a value or can step to another term (progress) of the same type (preservation).The judgment Γ ⊢ : is defined in Fig. 7 without the grey boxes, and for clarity we use for types.
We prove type safety by induction on the length of the sequence of steps comprising ↩→ * ′ , using the preservation and progress lemmas.

Progress
The progress lemma says that a well-typed term is either a value or steps to some other term.L 5.2.(Progress) If ⊢ : , then is a value or ↩→ ′ for some ′ .
Proof of progress requires a Canonical Forms lemma (Lemma 5.3) which describes the shape of well-typed values and some key properties about the built-in Primitives (Lemma 5.5).We also implicitly use an Inversion of Typing lemma (Lemma 5.4) which describes the shape of the type of well-typed terms and its subterms.For , unlike , this lemma is trivial because the typing relation is syntax-directed.(6) If Γ ⊢ Λ : .: , then there is some type ′ and kind such that = ∀ : .′ and ′ : , Γ ⊢ [ ′ / ] : ′ [ ′ / ] for some ′ ∉ Γ. (7) If Γ ⊢ let = in : , then there is some type and ∉ Γ such that Γ ⊢ : and : , Γ ⊢ [ / ] : .(8) If Γ ⊢ : : , then = ⌊ ⌋ and Γ ⊢ : .Lemmas 5.3 and 5.4 are proved without induction by inspection of the derivation tree, while lemma 5.5 relies on the Primitives Requirement 1.

Preservation
The preservation lemma states that typing is preserved by evaluation.The proof is by structural induction on the derivation of the typing judgment.We use the determinism of the operational semantics (Lemma 3.1) and the canonical forms lemma to case split on to determine ′ .The interesting cases are for T A and T TA .For applications of primitives, preservation requires the Primitives Lemma 5.5, while the general case needs a Substitution Lemma A.7.
Substitution Lemma To prove type preservation when a lambda or type abstraction is applied, we proved that the substituted result has the same type, as established by the substitution lemma: The proof goes by induction on the derivation tree.Because we encoded our typing rules using cofinite quantification ( § 4) the proof does not require a renaming lemma, but the rules that lookup environments (rules T V and WF V ) do need a lemma the below Weakening Lemma 5.8.Next, let's see the three main ways in which the proof of Lemma 6.2 differs from .

Inversion of Typing Judgments
The vertical lined region of Fig. 1 accounts for the fact that, due to subtyping chains, the typing judgment in is not syntax-directed.First, we establish that subtyping is transitive The proof consists of a case-split on the possible rules for Γ ⊢ 1 2 and Γ ⊢ 2 3 .When the last rule used in the former is S W and the latter is S B , we require the Substitution Lemma 6.6.As Aydemir et al. [2005], we use the Narrowing Lemma 6.8 for the transitivity for function types.
Inverting Typing Judgments We use the transitivity of subtyping to prove some non-trivial lemmas that let us "invert" the typing judgments to recover information about the underlying terms and types.We describe the non-trivial case which pertains to type and value abstractions: (2) If Γ ⊢ (Λ 1 : 1 .) : ∀ : .and ⊢ Γ, then for every ′ ∉ Γ we have ′ : If Γ ⊢ ( . ) : : → , then we cannot directly invert the typing judgment to get a typing for the body of . .Perhaps the last rule used was T S , and inversion only tells us that there exists a type 1 such that Γ ⊢ ( . ) : 1 and Γ ⊢ 1 : → .Inverting again, we may in fact find a chain of types +1 • • • 2 1 which can be arbitrarily long.But the proof tree must be finite so eventually we find a type : → such that Γ ⊢ ( .

Substitution Lemma
The main result in the diagonal lined region of Fig. 1 is the Substitution Lemma.The biggest difference between the and metatheories is the introduction of a mutual dependency between the lemmas for typing and subtyping judgments.Due to this dependency, the substitution lemma, and the weakening lemma on which it depends must now be proven in a mutually recursive form for both typing and subtyping judgments: The main difficulty arises in substituting some type for variable in Γ 1 , : , Γ 2 ⊢ { 1 : } { 2 : } because we must deal with strengthening by the refinements and respectively.As with the metatheory, the proof of the substitution lemma does not require renaming, but does require a lemmas that let us weaken environments (Lemma 6.7) in typing and subtyping judgments.
The proof is by mutual induction on the derivation of the typing and subtyping judgments.

Narrowing
The narrowing lemma says that whenever we have a judgment where a binding : appears in the binding environment, we can replace by any subtype .The intuition here is that the judgment holds under the replacement because we are making the context more specific.
The narrowing proof requires an Exact Typing Lemma 6.9 which says that a subtyping judgment Γ ⊢ is preserved after selfification on both types.Similarly whenever we can type a value at type then we also type at the type selfified by .L 6.9. and Γ ⊢ : , then Γ ⊢ : self ( , , ).

REFINED DATA PROPOSITIONS
In § 8 we will present how type soundness is encoded in L H .Here we present refined data propositions, a novel feature of L H that made such a meta-theoretic development possible.Intuitively, refined data propositions encode C -style inductive predicates to permit constructive reasoning about potentially non-terminating properties, as required for metatheoretic developments.(Parker et al. [2019] developed a meta-theoretic proof in L H without refined data propositions, but assumed a terminating evaluation relation; see § 9.) Refined data propositions encode inductive predicates in L H by refining Haskell's data types, allowing the programmer to write plain Haskell functions to provide constructive proofs for user-defined propositions.Here, for exposition, we present the four steps we followed in the mechanization of to define the "hastype" proposition and then use it to type the primitive one.
Step 1: Reifying Propositions as Data Our first step is to represent the different propositions of interest as plain Haskell data.For example, we can define the following types (with the suffix Pr for "proposition"): data HasTyPr = HasTy Env Expr Type data IsSubTyPr = IsSubTy Env Type Type Thus, HasTy e t and IsSubTy t t' respectively represent the propositions that e can be typed as t under environment and that t is a subtype of t' under .
Step 2: Reifying Evidence as Data Next, we reify evidence, i.e. derivation trees, also as data by defining Haskell data types with a single constructor per derivation rule.For example, we define the Haskell data type HasTyEv to encode the typing rules of Fig. 7, using a single constructor, whose name matches the corresponding rule: Step 3: Relating Evidence to its Propositions Next, we specify the relationship between the evidence and the proposition that it establishes, via a refinement-level uninterpreted function that maps evidence to its proposition: measure hasTyEvPr :: HasTyEv → HasTyPr measure isSubTyEvPr :: IsSubTyEv → IsSubTyPr The above signatures declare that hasTyEvPr (resp.isSubTyEvPr) is a refinement-level function that maps has-type (resp.is-subtype) evidence to its corresponding proposition.We can now use these uninterpreted functions to define type aliases that denote well-formed evidence that establishes a proposition.For example, consider the (refined) type aliases Note that the first three steps have only defined separate data types for propositions and evidence, and specified the relationship between them via uninterpreted functions in the refinement logic.
Step 4: Refining Evidence to Establish Propositions Our final step is to implement the relationship between evidence and propositions refining the types of the evidence data constructors (rules) with input types (pre-conditions) that require the rules' premises hold, and output types (postconditions) that ensure the rules' conclusions.For example, we connect the evidence and proposition for the typing relation by refining the data constructors for HasTyEv using the premises and conclusions of their respecting typing rule from Fig. 7.The constructors TPrim and TSub respectively encode the rules T P and T S rules (with the latter being simplified here to elide the well-formedness of t').The refinements on the input types, which encode the premises of the rules, are checked whenever these constructors are used.The refinement on the output type (being evidence of a specific proposition) is axiomatized to encode the conclusion of the rules.Consider the type for TSub.The first line says "for all , , , ′ ", the second line (premise) says, "given evidence that ⊢ : and ⊢ ′ ", and the last line (conclusion) says the constructor returns "evidence that ⊢ : ′ ".
Programs as Constructive Proofs Thus, the constructor refinements crucially ensure that only well-formed pieces of evidence can be constructed, and simultaneously, precisely track the proposition established by the evidence.This lets the programmer write plain Haskell terms as constructive proofs, and L H ensures that those terms indeed establish the proposition stipulated by their type.For example, the below Haskell term is proof that the literal 1 has the type Int{ : = 1} If instead, the programmer modified their code to oneTy = TPrim Empty (PInt 2), L H would reject the program as the modified evidence does not establish the proposition described in the type.

MECHANIZATION
We have mechanized soundness of in L H .The mechanization, submitted as anonymous supplementary material, uses refined data propositions ( § 7) to specify the static (e.g.typing, subtyping, well-formedness) and dynamic (i.e.small-step transitions and their closure) semantics of .The mechanization is simplified by SMT-automation ( § 8.1), uses a co-finite encoding for reasoning about variables ( § 8.2), and comprises of proofs implemented as recursive functions that constructively manipulate the evidence to establish propositions by induction ( § 8.3).Note that while Haskell types are inhabited by diverging ⊥ values, L H 's totality, termination and type checks ensure that all cases are handled, the induction (recursion) is well-founded, and that the proofs (programs) indeed establish the given propositions (types).

SMT Solvers and Set Theory
The most tedious part in mechanization of metatheories is the establishment of invariants about variables, for example uniqueness and freshness.L H offers a built-in, SMT automated support for the theory of sets, which simplifies establishing such invariants.
Set of Free Variables Our proof mechanization defines the Haskell function fv that returns the Set of free variable names that appear in its argument.In the above (incomplete) definition, S is used to qualify the standard Data.Set Haskell library.L H embeds the functions of Data.Set to SMT set operators (encoded as a map to booleans).For example, S.union is treated as the logical set union operator ∪.Further, we lift fv into the refinement logic using the measure fv annotation.The measure definition defines the logical function fv in the logic in a way that lets the SMT solver reason about the semantics of fv in a decidable fashion, as an uninterpreted function refining the type of each Expr data constructor [Jhala and Vazou 2021].This embedding, combined with the SMT solver's support for the theory of sets, lets L H prove properties about expressions' free variables "for free".
Intrinsic Verification For example, consider the function subFV x vx e which substitutes the free variable x with vx in e.The refinement type of subFV describes the free variables of the result.The refinement type post condition specifies that the free variables after substitution is a subset of the free variables on the two argument expressions, excluding the proof (no user aid is required) and, importantly, the specification is automatically established each time the function subFV is called.So, the user does not have to provide explicit hints to reason about free variables of substituted expressions.The specification of subFV shows another example of SMT-based proof simplification: the Haskell boolean function isVal is another measure that defines when an expression is a value.By doing so, we can intrinsically prove that that value property is preserved by substitution, as stated by the second (implication) conjunct in the output of subFV.
Freshness L H 's support for sets simplifies defining a fresh function, which is often challenging1 .fresh xs returns a variable that provably does not belong to its input xs.The fresh function returns n: the maximum element of the set increased by one.To compute the maximum element we convert the set to a list and use the inductively defined maxs functions.To prove fresh's intrinsic specification we use an extrinsic, i.e. explicit, lemma above _ max n xs' that, via the (?) combinator of type a → b → a, tells L H that n is not in the set xs.This extrinsic lemma is itself trivially proved by induction on xs and SMT automation.

Co-finite antification
To encode the rules that need a fresh free variable name we use the co-finite quantification of Aydemir et al. [2008], as discussed in § 4. Figure 9 presents this encoding using the T A rule as an example.The standard abstraction rule (rule T A E in § 4) requires to provide a concrete fresh name, which is encoded in the second line of TAbsEx as the y:{VName | y ∉ dom } argument.The co-finitely quantified encoding of the rule TAbs, instead, states that there exists a specified finite set of excluded names, namely l, and requires that the sub-derivation holds for any name y that does not belong in l.That is, the premise is turned into a function that, given the name y, returns the subderivation.This encoding greatly simplifies our mechanization, since the premises are no more tied to concrete names, eliminating the need for renaming lemmas.We will often take l to be the domain of the environment, but the ability to choose l gives us the flexibility when constructing derivations to exclude additional names that clash with another part of a proof.

Inductive Proofs as Recursive Functions
The majority of our proofs are by induction on derivations.These proofs are written as recursive Haskell functions that operate over the refined data types reifying the respective derivations.L H ensures the proofs are valid by checking that they are inductive (i.e. the recursion is  well-founded), handle all cases (i.e. the function is total) and establish the desired properties (i.e.witnesses the appropriate proposition.) Preservation (Theorem 6.3) relates the HasTy data proposition of § 7 with a Step data proposition that encodes Fig. 4 and is proved by induction on the type derivation tree.The subtyping case requires an induction while the primitive case is impossible (Lemma 3.1): In the TSub case we note that L H knows that the expression argument _ e is equal to the the subtyping parameter e.Further, the termination checker will ensure that the inductive call happens on the smaller derivation subtree.The TPrim case goes by contradiction since primitives cannot step.We separately proved that values cannot step in the lemValStep lemma, which here is combined with the fact that e is a value to allow the call of the false-precondition impossible.Finally, L H 's totality checker ensures all the cases of HasTypEv are covered, and the termination checker ensures the proof is well-founded.
Progress (Theorem 6.2) ensures that a well-typed expression is a value or there exists an expression to which it steps.To express this claim we used Haskell's Either to encode disjunction that contain pairs (refined to be dependent) to encode existentials.The proofs of the TSub and TPrim cases are easily done by, respectively, an inductive call and establishing is-Value.The more interesting cases require us to case-split on the inductive call in order to get access to the existential witness.
Soundness (Theorem 6.1) ensures that a well-typed expression will not get stuck, that is, it will either reach a value or keep evaluating.We reify evaluation sequences with a data proposition Steps e 0 e with a reflexive and a transitive (recursive) constructor.Our soundness proof goes by induction on the evaluation sequence.soundness _ e 0 t _ e e 0 _ has _ t e 0 _ evals _ e = case e 0 _ evals _ e of Refl e 0 → progress e 0 t e 0 _ has _ t --0 = AddStep e 0 e 1 e 0 _ step _ e 1 e e 1 _ eval _ e → --0 ↩→ 1 ↩→ * soundness e 1 t e (preservation e 0 t e 0 _ has _ t e 1 e 0 _ step _ e 1 ) e 1 _ eval _ e The reflexive case is proved by progress.In the inductive case the evaluation sequence is 0 ↩→ 1 ↩→ * and the proof goes by induction, using preservation to ensure that 1 is typed.

Mechanization Details
We provide a full, mechanically checked proof of the metatheory in § 5 and § 6.The only facts that are assumed are the Requirements 1 and 2, resp.on built-in primitives and the implication relation.

Representing Binders
In our mechanization, we use the locally-nameless representation [Aydemir et al. 2008;Charguéraud 2012].Free variables and bound variables are taken to be separate syntactic objects, so we do not need to worry about alpha renaming of free variables to avoid capture in substitutions.We also use de Bruijn indices only for bound variables.This enables us to avoid taking binder names into account in the strengthen function used to define substitution (Fig. 5).

Quantitative Results
In Table 1 we give the empirical details of our metatheory, which was checked using L H version 0.8.10.7.1 and a Lenovo ThinkPad T15p laptop with an Intel Core i7-11800H processor with 8 physical cores and 32 GB of RAM.Our mechanized proof is substantial, spanning about 9400 lines distributed over about 35 files.Currently, the whole proof can be checked in about 30 minutes, which can make interactive development difficult.While incremental modular checking provides a modicum of interactivity, improving the ergonomics, i.e. verification time and providing actionable error messages, remains an important direction for future work.

RELATED WORK
We discuss the most closely related work on the meta-theory of unrefined and refined type systems.Representing Binders Our development for ( § 5) follows the standard presentation of System F's metatheory by Pierce [2002].The main difference between the two metatheories is that ours includes well-formedness of types and environments, which help with mechanization [Rémy 2021] and are crucial when formalizing refinements.One of the main challenges in mechanization of metatheories is the syntactic representation of variables and binders [Aydemir et al. 2005].The named representation has severe difficulties because of variable capturing substitutions and the nameless (a.k.a.de Bruijn) requires heavy index shifting.The variable representation of is locally nameless representation [Aydemir et al. 2008;Pollack 1993], that is, free variables are named, but the bound variables are represented by syntactically distinct deBruijn indices.We chose this representation because it clearly addresses the following two problems with named bound variables but nevertheless our metatheory still resembles the paper and pencil proofs (that we performed before mechanization): First, when different refinements are strengthened (as in Fig. 5) the variable capturing problem reappears because we are substituting underneath a binder.Second, subtyping usually permits alpha-renaming of variables, which breaks a required invariant that each derivation tree is a valid tree after erasure.
Hybrid & Contract Systems Flanagan [2006] formalizes a monomorphic lambda calculus with refinement types that differs from our in three ways.First, the denotational soundness methodology of Flanagan [2006] connects subtyping with expression evaluation.We could not follow this approach because encoding type denotations as a data proposition requires a negative occurrence ( § 4.4).Second, in [Flanagan 2006] type checking is hybrid: the developed system is undecidable and inserts runtime casts when subtyping cannot be statically decided.Third, the original system lacks polymorphism.Sekiyama et al. [2017] extended hybrid types with polymorphism, but unlike , their system does not support semantic subtyping.For example, consider a divide by zeroerror.The refined types for div and 0 could be given by div :: Int → Int{ : ≠ 0} → Int and 0 :: Int{ : = 0}.This system will compile div 1 0 by inserting a cast on 0: Int{ : = 0} ⇒ Int{ : ≠ 0} , causing a definite runtime failure that could have easily been prevented statically.Having removed semantic subtyping, the metatheory of [Sekiyama et al. 2017] is highly simplified.Static refinement type systems (as summarized by Jhala and Vazou [2021]) usually restrict the definition of predicates to quantifier-free first-order formulae that can be decided by SMT solvers.This restriction is not preserved by evaluation that can substitute variables with any value, thus allowing expressions that cannot be encoded in decidable logics, like lambdas, to seep into the predicates of types.In contrast, we allow predicates to be any language term (including lambdas) to prove soundness via preservation and progress: our meta-theoretical results trivially apply to systems that, for efficiency of implementation, restrict their source languages.Finally, none of the above systems (hybrid, contracts or static refinement types) come with a machine checked soundness proof.
Refinement Types in Coq Our soundness formalization follows the axiomatized implication relation of Lehmann and Tanter [2016] that decides subtyping (our rule S B ) by without formally connecting implication and expression evaluation.Lehmann and Tanter [2016]'s Coq formalization of a monomorphic lambda calculus with refinement types differs from in two ways.First, their axiomatized implication allows them to arbitrarily restrict the language of refinements.We allow refinements to be arbitrary program terms and intend, in the future, to connect our axioms to SMT solvers or other oracles.Second, includes language features like polymorphism, existentials, and selfification which are critical for path-and context-sensitive refinement typing, but make the metatheory more challenging.Hamza et al. [2019] present System FR, a polymorphic, refined language with a mechanized metatheory of about 30K lines of Coq.Compared to our system, their notion of subtyping is not semantic, but relies on a reducibility relation.For example, even though System FR will deduce that Pos is a subtype of Int, it will fail to derive that Int → Pos is subtype of Pos → Int as reduction-based subtyping cannot reason about contra-variance.Because of this more restrictive notion of subtyping, their mechanization does not require either the indirection of denotational soundness or the use of an implication proving oracle.Further, System FR's support for polymorphism is limited in that it disallows refinements on type variables, thereby precluding many practically useful specifications.

Metatheory in L H
LWeb [Parker et al. 2019] also used L H to prove metatheory, concretely non-interference of LWeb , a core calculus that extends the LIO formalism with database access.The LWeb proof did not use refined data propositions, which were not present at the time of its development, and thus it has two major weaknesses compared to our present development.First, LWeb assumes termination of LWeb 's evaluation function; without refined data propositions metatheory can be developed only over terminating functions.This was not a critical limitation since non-interference was only proved for terminating programs.However, in our proof the requirement that evaluation of terminates would be too strict.In our encoding with refined data propositions such an assumption was not required.Second, the LWeb development is not constructive: the structure of an assumed evaluation tree is logically inspected instead of the more natural case splitting permitted only with refined data propositions.This constructive way to develop metatheories is more compact (e.g.there is no need to logically inspect the structure of the derivation trees) and more akin to the standard meta-theoretic developments of constructive tools like C and I .

CONCLUSIONS & FUTURE WORK
We presented and formalized soundness of , a core refinement calculus that combines semantic subtyping, existential types, and parametric polymorphism, which are critical for practical refinement typing but whose combination has never been formalized.Our meta-theory is mechanized in L H , using the novel feature of refined data propositions to reify e.g.typing and evaluation derivations as (refined) Haskell datatypes, and exploits SMT to automate various tedious invariants about variables.We fully expect our proof can be mechanized in specialized proof systems like A [Norell 2007], C [Bertot and Castéran 2004] or I [Nipkow et al. 2002] or those equipped with SMT-based automation like D [Leino 2010], or F* [Martínez et al. 2019].Our goal here is not to compare against such systems.Instead, our primary contribution is to, for the first time, establish the soundness of the combination of features critical for practical refinement typing, and secondarily, show that such a proof can be mechanized as a plain program with refinement types.Looking ahead, we envision two lines of work on mechanizing metatheory of and with refinement types.

Mechanization of Refinements
covers a crucial but small fragment of the features of modern refinement type checkers.It would be interesting to extend the language to include features like refined datatypes, and abstract and bounded refinements.Similarly, our current work axiomatizes the requirements of the semantic implication checker (i.e.SMT solver).It would be interesting to implement a solver and verify that it satisfies that contract, or alternatively, show how proof certificates [Necula 1997] could be used in place of such axioms.

Mechanization with Refinements
While this work shows that non-trivial meta-theoretic proofs are possible with SMT-based refinement types, our experience is that much remains to make such developments pleasant.For example, programming would be far more convenient with support for automatically splitting cases or filling in holes as done in Agda [Norell 2007] and envisioned by Redmond et al. [2021].Similarly, when a proof fails, the user has little choice but to think really hard about the internal proof state and what extra lemmas are needed to prove their goal.Finally, the stately pace of verification -9400 lines across 35 files take about 30 minutes -hinders interactive development.Thus, rapid incremental checking, lightweight synthesis, and actionable error messages would go a long way towards improving the ergonomics of verification, and hence remain important directions for future work.
Lemma.Then by the inductive hypothesis applied to 1 , we conclude that either ′ is a value or ′ ↩→ ′′ for some ′′ .
A.1 Progress L A.2. (Progress) If ⊢ : then is a value or ↩→ ′ for some ′ .

P
. We proceed by induction of the structure of ⊢ : .In the cases of rule T P , T V , T A , or T TA , is a value.Case T A : We have ⊢ : where ≡ 1 2 .Inverting, we have that there exists some type 2 such that ⊢ 1 : 2 → and ⊢ 2 : 2 .We split on five possible cases for the structure of 1 and 2 .First, suppose 1 ≡ .0 and 2 is a value.Then by rule E A A , ≡ .0 2 ↩→ 0 [ 2 / ].Second, suppose 1 ≡ .0 and 2 is not a value.Then by the inductive hypothesis, there exists a term ′ 2 such that 2 ↩→ ′ 2 .Then by rule E A 2 ≡ .0 2 ↩→ .0 ′ 2 .Third, suppose 1 ≡ , a built in primitive and 2 is a value.Then by rule E P , ≡ 2 ↩→ ( , 2 ), which is well-defined by the primitives lemma.Fourth, suppose 1 ≡ and 2 is not a value.Then by the inductive hypothesis, there exists a term ′ 2 such that 2 ↩→ ′ 2 .Then by rule E A 2 ≡ 2 ↩→ ′ 2 .Finally, by the canonical forms lemma, 1 cannot be any other value, so it must not be a value.Then by the inductive hypothesis, there is a term ′ 1 such that 1 ↩→ ′ 1 .Then by rule E A 1, ≡ 1 2 ↩→ ′ 1 2 .Case T TA : We have ⊢ : where ≡ 1 [ ] and ≡ [⌊ ⌋/ ].Inverting, we have that ⊢ 1 : ∀ : . .We split on three cases for the structure of 1 .First, suppose 1 ≡ Λ ′ : ′ .0 .Then by rule E A TA , ≡ Λ ′ : ′ .0 [ ] ↩→ 0 [ / ′ ].Second, suppose 1 ≡ , a built in primitive.Then by rule E P T, ≡ [ ] ↩→ ( , ⌊ ⌋), which is well-defined by the primitives lemma.Finally, by the canonical forms lemma, 1 cannot be any other form of value, so it must not be a value.Then by the inductive hypothesis, there is a term ′ 1 such that 1 ↩→ ′ 1 .Then by rule E A T ≡ 1 [ ] ↩→ ′ 1 [ ]. Case T L : We have ⊢ : where ≡ let = 1 in 2 .Inverting, we have that ⊢ 1 : 1 for some type 1 .By the inductive hypothesis, either 1 is a value or there is a term ′ 1 such that 1 ↩→ ′ 1 .In the former case, rule E L V gives us ≡ let = 1 in 2 ↩→ 2 [ 1 / ].In the latter case, by rule E L , ≡ let = 1 in 2 ↩→ let = ′ 1 in 2 .Case T A : We have ⊢ : where ≡ 1 : .Inverting, we have the ⊢ 1 : and = ⌊ ⌋.By the inductive hypothesis, either 1 is a value or there is a term ′ 1 such that 1 ↩→ ′ 1 .In the former case, by rule E A V, ≡ 1 : ↩→ 1 .In the latter case, rule E A gives us ≡ 1 : ′ 1 : .The progress proof is substantially the same for .The only difference is that there is another straightforward inductive case for rule T S .Case T P : We have ≡ and Γ ′ , : , Γ ⊢ : ⌊ty( )⌋.Neither nor ty( ) has any free variables, so each is unchanged under substitution.Then by rule T P we conclude Γ ′ [⌊ ⌋/ ], Γ ⊢ : ⌊ty( )⌋ because the environment may be chosen arbitrarily.Case T V : We have ≡ ; by inversion, we get that : ∈ Γ ′ , : , Γ.We must have ≠ so there are two cases to consider for where can appear in the environment.If : ∈ Γ, then cannot contain as a free variable because is bound first in the environment (which grows from right to left).

B PROOFS OF SYSTEM RF SOUNDNESS
We present the proofs in this appendix in the same order Inversion of Typing Judgments.In the yellow region of Figure 1, we discuss how the fact that the typing judgment is no longer syntax-directed leads to an involved proof for the inversion lemma below.First, we need a lemma about subtyping:
The definition stipulates that the type HasTy e t is inhabited by evidence (of type HasTyEv) that establishes the typing proposition HasTyPr e t.Similarly IsSubTy t t' is inhabited by evidence (of type IsSubTyEv) that establishes the sub-typing proposition IsSubTyPr t t'.

Table 1 .
Empirical details of our mechanization.We partition the development into sets of modules pertaining to different region of Fig.1, and for each region separate the lines of specification (e.g.definitions and lemma statements) from those needed for proofs.