Hello everyone, I’m currently trying to develop a ...
# compiler
y
Hello everyone, I’m currently trying to develop a Kotlin/CLR compiler (by reusing official Kotlin compiler components; [project repo](https://github.com/Nyayurin/KotlinForCLR)). Recently, I’ve been experimenting with using Avalonia together with Avalonia.Markup.Declarative from Kotlin. However, when I try to compile the following code, I encounter errors(see the attchment)
Copy code
package yurin.mal

import Avalonia.Controls.Button
import Avalonia.Controls.StackPanel
import Avalonia.Controls.TextBlock
import Avalonia.Markup.Declarative.Children
import Avalonia.Markup.Declarative.ComponentBase
import Avalonia.Markup.Declarative.OnClick
import Avalonia.Markup.Declarative.Text

class MALComponent : ComponentBase() {
	private var count = 0
	override fun Build(): Any {
		val stackPanel = StackPanel()
		stackPanel.Children()
		val textBlock = TextBlock()
		textBlock.Text("Avalonia with kotlin demo")
		stackPanel.Children!!.Add(textBlock)
		val button = Button()
		button.OnClick { _ ->
			count++
			StateHasChanged()
		}
		stackPanel.Children!!.Add(button)
		return stackPanel
	}
}
The first issue is that calling
stackPanel.Children()
results in a
receiver type mismatch
error when resolving the
Children
function. The second issue is the same kind of problem with
textBlock.Text("Avalonia with kotlin demo")
. The third issue is that calling
button.OnClick { _ -> count++; StateHasChanged() }
also fails due to function resolution. The fourth issue is a consequence of the third one. I’ve already implemented a stub generator that parses CLR assemblies into Kotlin source code for IDEA to use for syntax analysis and code completion. Under IDEA, when using these stubs, the functions are resolved correctly and this code shows no errors. For the first issue, IDEA successfully resolves the top-level
Children
function:
Copy code
public fun <reified TPanel : Avalonia.Controls.Panel?> TPanel?.Children(vararg children: Avalonia.Controls.Control?): TPanel? = kotlin.TODO()
For the second issue, IDEA successfully resolves the top-level
Text
function:
Copy code
public fun <reified T : Avalonia.Controls.TextBlock?> T?.Text(
    value: kotlin.String?,
    bindingMode: System.Nullable1<Avalonia.Data.BindingMode?>? = kotlin.TODO(),
    converter: Avalonia.Data.Converters.IValueConverter? = kotlin.TODO(),
    bindingSource: kotlin.Any? = kotlin.TODO(),
    ps: kotlin.String? = kotlin.TODO()
): T? = kotlin.TODO()
For the third issue, IDEA successfully resolves the top-level
OnClick
function:
Copy code
public fun <reified T : Avalonia.Controls.Button?> T?.OnClick(
    action: System.Action1<Avalonia.Interactivity.RoutedEventArgs?>?
): T? = kotlin.TODO()
But in the actual compiler, it instead resolves to the member function of `Button`:
Copy code
... class Button ... {
    protected open fun OnClick(): kotlin.Unit? = kotlin.TODO()
}
I tried debugging the corresponding
FirFunction
objects and their properties all look normal. I also checked the inheritance hierarchy of the related
FirRegularClass
objects and confirmed there’s no issue there. I also tried removing
reified
and nullability from these functions, but the result is still the same. Here is the generated raw FIR:
Copy code
FILE: MALComponent.kt
    public final? class MALComponent : ComponentBase {
        public? constructor(): R|yurin/mal/MALComponent| {
            super<ComponentBase>()
        }

        private final? var count: <implicit> = IntegerLiteral(0)
            private get(): <implicit>
            private set(value: <implicit>): R|kotlin/Unit|

        public? open? override fun Build(): Any {
            lval stackPanel: <implicit> = StackPanel#()
            stackPanel#.Children#()
            lval textBlock: <implicit> = TextBlock#()
            textBlock#.Text#(String(Avalonia with kotlin demo))
            stackPanel#.Children#!!.Add#(textBlock#)
            lval button: <implicit> = Button#()
            button#.OnClick#(<L> = OnClick@fun <implicit>.<anonymous>(<unused var>: <implicit>): <implicit> <inline=Unknown>  {
                count#++
                StateHasChanged#()
            }
            )
            stackPanel#.Children#!!.Add#(button#)
            ^Build stackPanel#
        }

    }
And the FIR after processing:
Copy code
FILE: MALComponent.kt
    public final class MALComponent : R|Avalonia/Markup/Declarative/ComponentBase| {
        public constructor(): R|yurin/mal/MALComponent| {
            super<R|Avalonia/Markup/Declarative/ComponentBase|>()
        }

        private final var count: R|kotlin/Int| = Int(0)
            private get(): R|kotlin/Int|
            private set(value: R|kotlin/Int|): R|kotlin/Unit|

        protected open override fun Build(): R|kotlin/Any| {
            lval stackPanel: R|Avalonia/Controls/StackPanel| = R|Avalonia/Controls/StackPanel|()
            R|<local>/stackPanel|.<Ambiguity: Children, [Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children, Avalonia/Markup/Declarative/Children]>#()
            lval textBlock: R|Avalonia/Controls/TextBlock| = R|Avalonia/Controls/TextBlock|()
            R|<local>/textBlock|.<Ambiguity: Text, [Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text, Avalonia/Markup/Declarative/Text]>#(String(Avalonia with kotlin demo))
            R|<local>/stackPanel|.R|Avalonia/Controls/Panel.Children|!!.R|System/Collections/IList.Add|(R|<local>/textBlock|)
            lval button: R|Avalonia/Controls/Button| = R|Avalonia/Controls/Button|()
            R|<local>/button|.R|Avalonia/Controls/Button.OnClick<Inapplicable(INAPPLICABLE_ARGUMENTS_MAPPING_ERROR): Avalonia/Controls/Button.OnClick>#|(<L> = OnClick@fun <anonymous>(<unused var>: <ERROR TYPE REF: Cannot infer type for parameter <unused var>>): <ERROR TYPE REF: Cannot infer type variable TypeVariable(_L)> <inline=Unknown>  {
                 {
                    lval <unary>: R|kotlin/Int| = this@R|yurin/mal/MALComponent|.R|yurin/mal/MALComponent.count|
                    this@R|yurin/mal/MALComponent|.R|yurin/mal/MALComponent.count| = R|<local>/<unary>|.R|kotlin/Int.inc|()
                    R|<local>/<unary>|
                }

                ^ this@R|yurin/mal/MALComponent|.R|Avalonia/Markup/Declarative/ComponentBase.StateHasChanged|()
            }
            )
            R|<local>/stackPanel|.R|Avalonia/Controls/Panel.Children|!!.R|System/Collections/IList.Add|(R|<local>/button|)
            ^Build R|<local>/stackPanel|
        }

    }
y
It's hard to pinpoint your exact problem without seeing your compiler code. Is that open-source, per chance? A reproducer with this exact example would be great, too!
y
Sure! Here is a minimal reproducible example repository: https://github.com/Nyayurin/KotlinAvaloniaDemo And here is the compiler source repository: https://github.com/Nyayurin/KotlinForCLR Prerequisites to run the minimal repro project: Install the
dotnet SDK
. Modify
-dotnet-version
in
./script/AvaloniaClrCompilation.ps1
(you can check your installed versions under
C:\Program Files\dotnet\shared\<http://Microsoft.NETCore.App|Microsoft.NETCore.App>\
). 1. Run the script
./script/AvaloniaClrCompilation.ps1
. 2. Modify
-dotnet-version
in
./script/AvaloniaClrCompilation.ps1
(you can check your installed versions under
C:\Program Files\dotnet\shared\<http://Microsoft.NETCore.App|Microsoft.NETCore.App>\
). 3. Run the script
./script/AvaloniaClrCompilation.ps1
.