Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validation error: Type parameter variance #743

Closed
lars-reimann opened this issue Nov 9, 2023 · 0 comments · Fixed by #852
Closed

Validation error: Type parameter variance #743

lars-reimann opened this issue Nov 9, 2023 · 0 comments · Fixed by #852
Assignees
Labels
released Included in a release validation ✔️ Improved or new static checks
Milestone

Comments

@lars-reimann
Copy link
Member

lars-reimann commented Nov 9, 2023

Is your feature request related to a problem?

Type parameter variance is currently not checked vs. how the type parameter is used.

Desired solution

Add validation.

Possible alternatives (optional)

No response

Screenshots (optional)

No response

Additional Context (optional)

Behavior in Kotlin:

abstract class List<in T, out S> {
    abstract val a: (a: S) -> T
    abstract val b: List<S, T>
    abstract val c: T
    
    abstract fun f(
        p: (a: T) -> S,
        q: S,
        r: (x: (a: S) -> T) -> ((a: T) -> S)
    ): T
}
Type parameter S is declared as 'out' but occurs in 'in' position in type (S) -> T
Type parameter T is declared as 'in' but occurs in 'out' position in type (S) -> T
Type parameter S is declared as 'out' but occurs in 'in' position in type List<S, T>
Type parameter T is declared as 'in' but occurs in 'out' position in type List<S, T>
Type parameter T is declared as 'in' but occurs in 'out' position in type T
Type parameter T is declared as 'in' but occurs in 'out' position in type (T) -> S
Type parameter S is declared as 'out' but occurs in 'in' position in type (T) -> S
Type parameter S is declared as 'out' but occurs in 'in' position in type S
Type parameter S is declared as 'out' but occurs in 'in' position in type ((S) -> T) -> (T) -> S
Type parameter T is declared as 'in' but occurs in 'out' position in type ((S) -> T) -> (T) -> S
Type parameter T is declared as 'in' but occurs in 'out' position in type ((S) -> T) -> (T) -> S
Type parameter S is declared as 'out' but occurs in 'in' position in type ((S) -> T) -> (T) -> S
Type parameter T is declared as 'in' but occurs in 'out' position in type T

Observations: Each level of callables beyond the first flips the variance of a type parameter. For the parameter p of f, for example, we have to supply a value of type T, which then flows out of the class into the function.

It's probably beneficial to think of (S) -> (T) as Function2<in S, out T> and generally figure out how nesting of in/out modifiers works when determining in vs. out positions. We also have to figure out how something like List<List<S, T>, List<S, T>> works.

abstract class List<in T, out S> {
    abstract val a: List<
    	List<S, T>,
    	List<T, S>
    >
}

No errors in the above program. First idea:

  1. Assign variance
.<
    in<in, out>,
    out<in, out>
>
  1. Replace out<...> constructs by .<> constructs
.<
    in<in, out>,
    .<in, out>
>
  1. Swap all variances inside an in<...> construct and replace it by a .<> construct
.<
    .<out, in>,
    .<in, out>
>
  1. Repeat 2./3. from inside to outside.

=> in swaps variance, out maintains variance.

Correct programs:

abstract class List<in T, out S> {
    abstract val a: List<
    	Int,
    	List<Int,
    		List<Int,
    			List<T, S>
    		>
    	>
    >
}

Erroneous programs:

abstract class List<T, out S> {
    abstract val a: List<
    	List<Int,
    		List<Int,
    			List<T, S>
    		>
    	>,
    	Int
    >
}
Type parameter S is declared as 'out' but occurs in 'invariant' position in type List<List<Int, List<Int, List<T, S>>>, Int>
  • Outer invariant enforces invariant
  • Outer in swaps in/out
  • Outer out maintains in/out
@lars-reimann lars-reimann added enhancement 💡 validation ✔️ Improved or new static checks labels Nov 9, 2023
@lars-reimann lars-reimann added this to DSL Nov 9, 2023
@github-project-automation github-project-automation bot moved this to Backlog in DSL Nov 9, 2023
@lars-reimann lars-reimann added this to the v0.6.0 milestone Nov 24, 2023
@lars-reimann lars-reimann self-assigned this Jan 15, 2024
@lars-reimann lars-reimann moved this from Backlog to Todo in DSL Jan 15, 2024
@lars-reimann lars-reimann linked a pull request Feb 2, 2024 that will close this issue
lars-reimann added a commit that referenced this issue Feb 2, 2024
Closes #743

### Summary of Changes

* Covariant type parameters must only be used in covariant positions.
* Contravariant type parameters must only be used in contravariant
positions.

This PR adds checks for this.
@github-project-automation github-project-automation bot moved this from Todo to ✔️ Done in DSL Feb 2, 2024
lars-reimann pushed a commit that referenced this issue Feb 12, 2024
## [0.7.0](v0.6.0...v0.7.0) (2024-02-12)

### Features

* apply type parameter substitutions of receiver type for member accesses ([#859](#859)) ([5780ed7](5780ed7)), closes [#23](#23)
* check position of usages of variant type parameters ([#852](#852)) ([a2672d7](a2672d7)), closes [#743](#743)
* check whether lower and upper bounds of a type parameter are compatible ([#885](#885)) ([2fc7fe6](2fc7fe6)), closes [#875](#875)
* check whether type parameter bounds are acyclic ([#886](#886)) ([bcf1a4b](bcf1a4b)), closes [#874](#874)
* check whether type parameter bounds are named types ([#878](#878)) ([d8b4168](d8b4168)), closes [#876](#876)
* error if type parameter has multiple lower/upper bounds ([#870](#870)) ([6035b76](6035b76)), closes [#867](#867)
* error if type parameter of class is used in static context ([#830](#830)) ([d5cf420](d5cf420)), closes [#809](#809)
* error if type parameters of functions are variant ([#869](#869)) ([9bf5fec](9bf5fec))
* handle type parameter types in type checker ([#884](#884)) ([6b6f738](6b6f738)), closes [#877](#877)
* handle type parameters (part 1) ([#856](#856)) ([8a35558](8a35558)), closes [#23](#23)
* improve type simplification ([#871](#871)) ([0daafb9](0daafb9))
* Memoization ([#827](#827)) ([d0a6c71](d0a6c71))
* null-safe call & indexed access ([#872](#872)) ([f1420a2](f1420a2)), closes [#857](#857) [#858](#858)
* optional type parameters ([#829](#829)) ([0e9f67a](0e9f67a)), closes [#739](#739)
* optionally generate code without runner integration ([#836](#836)) ([0ed9d6e](0ed9d6e)), closes [#831](#831)
* pre-load all relevant workspace files before executing pipeline ([#822](#822)) ([67ab766](67ab766))
* simplify computed types ([#866](#866)) ([bde3274](bde3274))
* simplify substitutions passed to `computeType` ([#873](#873)) ([aa444d4](aa444d4))
* substitute type parameters when checking assignability of overriding members ([#865](#865)) ([a1f24e0](a1f24e0)), closes [#862](#862)
* substitute type parameters when computing type of inherited members ([#864](#864)) ([33de1e8](33de1e8)), closes [#863](#863)
* support placeholder queries that only request a subset of data ([#826](#826)) ([1e39300](1e39300))
* type casts ([#838](#838)) ([66c3666](66c3666)), closes [#835](#835)

### Bug Fixes

* allow usage of covariant type parameters in own constructor ([#854](#854)) ([4ebae94](4ebae94))
* check for duplicate bounds if type parameters occur as right operand ([#882](#882)) ([8776ce0](8776ce0)), closes [#881](#881)
* generation of static class functions ([#832](#832)) ([57eac45](57eac45))
@lars-reimann lars-reimann added the released Included in a release label Feb 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
released Included in a release validation ✔️ Improved or new static checks
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

1 participant