Kotlin / Kotlin/dataframe

Fix compiler plugin losing generic upper-bound (`T : Geo`) after DataFrame transformations

Open
#905 2 comments 0 reactions 1 assignee Claimed by @koperagen View on GitHub
bug Compiler plugin
Dominant language
Kotlin
Stars
1.1k
Forks
83
Avg merge
4d 12h
Merged PRs (30d)
30

Description

Given API with type parameter bound to something:
```
interface Geo {
val geometry: Geometry
}
internal class GeoDataFrame(val df: DataFrame) {
fun update(f: DataFrame.() -> DataFrame): DataFrame = TODO()
}
```

Two things can go wrong in combination with compiler plugin
```
geo.update { remove { geometry } }
```
1. type of DataFrame after remove is not subtype of Geo. So `org.jetbrains.kotlinx.dataframe.plugin.extensions.FunctionCallTransformer#intercept` must somehow figure out that this supertype is needed. Note that incercept happens before call completion
2. even if type is preserved, there will be no longer such column. An additional checker should be there to report that column is missing
related to https://github.com/Kotlin/dataframe/issues/875

### Problem

Compiler plugin-generated local types do not preserve generic upper-bound constraints.

Example:
```
interface Geo {
val geometry: Int
}

internal class GeoDataFrame(val df: DataFrame) {
fun update(f: DataFrame.() -> DataFrame): DataFrame = TODO()
}

internal fun f(geo: GeoDataFrame) {
geo.update {
add("b") { 123 }
}
}
```
The compiler plugin infers a new local DataFrame type after `add`, but this type is no longer treated as satisfying the original `T : Geo` constraint.

As a result, the call fails with a type mismatch:

`DataFrame` is inferred, but `DataFrame` is expected.

### Expected

Compiler plugin-generated local types should preserve required upper-bound schema constraints when used inside generic APIs.

In this example, after adding a column, the resulting local type should still satisfy the `Geo` contract because the required `geometry` column remains present.

### Acceptance criteria

- Reproduce the issue with a generic `T : Geo` DataFrame wrapper
- Compiler plugin preserves upper-bound schema constraints when generated local type still contains required columns
- If an operation removes a required upper-bound column, the plugin reports a clear diagnostic
- Add tests for:
- adding a column while preserving upper-bound schema
- removing a required column and reporting an error
- Document the expected behavior for generic schema bounds if needed

### Motivation

Generic wrappers around `DataFrame` are a natural way to build domain-specific APIs.

If compiler plugin local types do not preserve upper-bound constraints, such wrappers become hard to use with common operations like `add`, `remove`, or `update`.

This is important for making compiler-plugin-based typed DataFrame workflows reliable before 1.0.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.