Tensegritics / ClojureDart

Clojure dialect for Flutter and Dart
1.43k stars 91 forks source link

Type cast error on latest blessed sha #319

Open valerauko opened 2 months ago

valerauko commented 2 months ago

Describe the bug

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following _TypeError was thrown building watch_widget_caaxye$1(dirty, state:
WatchState#3db53(subscribable: Instance of 'Cell', current value: null)):
type 'AlwaysStoppedAnimation<dynamic>' is not a subtype of type 'Animation<Color?>?' in type cast

The relevant error-causing widget was:
  watch_widget_caaxye$1
  watch_widget_caaxye$1:file:///home/valerauko/[redacted]/lib/cljd-out/[redacted].dart:214:24

When the exception was thrown, this was the stack:
#0      ifn_widget_18695hm$1.$_invoke$1 (package:[redacted]/cljd-out/[redacted]:29:133)
#1      ifn_widget_18695hm$1.$_invoke$0 (package:[redacted]/cljd-out/[redacted]:23:31)
#2      watch_widget_caaxye$1.$_build$2 (package:[redacted]/cljd-out/[redacted]:89:42)
#3      WatchState.build (package:[redacted]/cljd-out/cljd/flutter.dart:1792:67)
#4      StatefulElement.build (package:flutter/src/widgets/framework.dart:5599:27)

The offending widget is as follows

   (f/widget
    m/Center
    m/CircularProgressIndicator
    .valueColor
    (m/AlwaysStoppedAnimation color))

Does your problem persist after clj -M:cljd clean && flutter clean? Yes

To Reproduce After cljd upgrade the UI is full of the above exception

Expected behavior No type cast error

Additional info Not sure if related but there are also two dynamic warnings

DYNAMIC WARNING: can't resolve member begin on target type dynamic of library dart:core  at line: 678, column: 29, file: cljd/flutter.cljd
DYNAMIC WARNING: can't resolve member lerp on target type dynamic of library dart:core  at line: 678, column: 3, file: cljd/flutter.cljd
cgrand commented 2 months ago

the warnings are red herrings and are fixed on main

cgrand commented 2 months ago

How is color defined? If it's a compiler regression it looks like we lost the type of color and thus (m/AlwaysStoppedAnimation color) is not inferred to the right type. The most recent change affecting inference is https://github.com/Tensegritics/ClojureDart/commit/4e80ee1ca52fec788f9b5a555e8f44299a67de8c

cgrand commented 2 months ago

@valerauko provided form is not enough to repro. Can you share more? At least how color is introduced.

valerauko commented 2 months ago

@cgrand here's a full main.cljd. This works on previous sha but results in error on latest.

(ns repro.main
  (:require ["package:flutter/material.dart" :as m]
            [cljd.flutter :as f]))

(defn loading
  ([]
   (loading (m/Color. 0xff3c434d)))
  ([^m/Color color]
   (f/widget
    m/Center
    m/CircularProgressIndicator
    .valueColor
    (m/AlwaysStoppedAnimation. color))))

(defn main []
  (f/run
   m/MaterialApp
   .home
   (f/widget
    m/Center
    (loading))))
cgrand commented 2 months ago

@valerauko e05433c2999af00b19c1ea5c6093f639d1d7d4c5 is the culprit! I wasn't suspecting that. Will fix asap. Have you been able to work around the issue?

valerauko commented 2 months ago

For now, not upgrading to latest sha is the "workaround". It works fine on the previous blessed sha and I didn't see any critical changes that would require latest sha, so I can wait for the fix to be released~

cgrand commented 2 months ago

The afore-mentioned commit is sound. The expansion of f/widget changed and introduced an extra let. Thus (m/AlwaysStoppedAnimation. color) was extracted instead of being inline. When inline, it happened it stayed inline even in Dart. Thus leaving type inference to Dart. When extracted, ClojureDart does the type inference and is oblivious to type variables.

(deftest infer-type-args
  (let [e (MapEntry (str "a") (count "a"))]
    (is (instance? #/(MapEntry String int) e))))

fails currently, saying e is an instance of MapEntry<dynamic, dynamic>.

valerauko commented 2 months ago

Hmm but in my case, color is explicitly type hinted

cgrand commented 2 months ago

the problem is not with color but with the compiler failing (well not even trying) to infer the parametrized type of (m/AlwaysStoppedAnimation. color). As long as there was no let it was ok because the non-parametrized type never appeared and then the Dart compiler inferred the right one.

valerauko commented 5 days ago

I worked around this by changing the offending code as follows

(defn loading
  ([]
   (loading (m/Color. 0xff3c434d)))
  ([^m/Color color]
   (f/widget
    m/Center
    (m/CircularProgressIndicator
     .valueColor (m/AlwaysStoppedAnimation. color)))))

(eg manually added parens around the CircularProgressIndicator so it wouldn't rely on the f/widget expansion)

cgrand commented 4 days ago

Obvious workaround in hindsight.