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

Analysis error comparing value in generic function: The operator '<' isn't defined for the type 'Object'. Try defining the operator '<'. #48952

Closed
idraper opened this issue May 4, 2022 · 6 comments
Assignees
Labels
area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. improve-diagnostics Related to the quality of diagnostic messages P3 A lower priority bug or feature request

Comments

@idraper
Copy link

idraper commented May 4, 2022

Dart SDK version: 2.15.1 (stable) on windows_x64

I have a class that takes an object with some settings. I need the ability to override some settings with differing logic only in specific cases and have come up with a solution that the analyzer throws an error for. Here is a stripped-down version which demonstrates the problem:

typedef SettingOrFunc = int? Function<T>(ContainsValue<T> tag);

class ContainsValue<T> {
  final T val;
  ContainsValue(this.val);
}

class UsesSetting {
  final SettingOrFunc? setting;
  UsesSetting({this.setting});

  /// Do stuff with wrappedValue here,
  /// for my project, if wrappedValue is/returns null,
  /// default values are used otherwise uses the wrappedValue's return.
}

void main(List<String> args) {
  UsesSetting(
    setting: <num>(t) => t.val == 50 ? 0 : null, // is fine
  );
  UsesSetting(
    setting: <num>(t) => t.val < 50 ? 0 : null, // throws analysis error:
                                                // The operator '<' isn't defined for the type 'Object'.
                                                // Try defining the operator '<'. dart(undefined_operator)
  );
}

Which throws the error (when run - or just highlighting from the analyzer):

main.dart:22:37: Error: The operator '<' isn't defined for the class 'Object?'.       
 - 'Object' is from 'dart:core'.
Try correcting the operator to an existing operator, or defining a '<' operator.      
    wrappedValue: <num>(t) => t.val < 50 ? 0 : null,  // throws static analysis error:

At first, I thought this may have been because Dart is unsound and the value is within an object, but then slightly modified to just use the generic:

typedef SettingOrFunc = int? Function<T>(T val);

class UsesSetting {
  final SettingOrFunc? setting;
  UsesSetting({this.setting});

  /// Do stuff with setting here,
  /// for my project, if wrappedValue is/returns null,
  /// default values are used otherwise uses the wrappedValue's return.
}

void main(List<String> args) {
  UsesSetting(
    setting: <num>(t) => t == 50 ? 0 : null, // is fine
  );
  UsesSetting(
    setting: <num>(t) => t < 50 ? 0 : null, // throws analysis error:
                                            // The operator '<' isn't defined for the type 'Object'.
                                            // Try defining the operator '<'. dart(undefined_operator)
  );
}

which throws (basically the same):

main.dart:22:32: Error: The operator '<' isn't defined for the class 'Object?'.
 - 'Object' is from 'dart:core'.
Try correcting the operator to an existing operator, or defining a '<' operator.
    setting: <num>(t) => t.val < 50 ? 0 : null, // throws analysis error:

My belief is this shouldn't be throwing an error. If I highlight over the value (in the first example) or use code-completion inside the function body the analysis tools know the proper type. When not using the typedef it also has the same error.

@idraper idraper changed the title Error comparing generic value in typedef function: The operator '<' isn't defined for the type 'Object'. Try defining the operator '<'. Analysis error comparing value in generic function: The operator '<' isn't defined for the type 'Object'. Try defining the operator '<'. May 4, 2022
@idraper
Copy link
Author

idraper commented May 4, 2022

Interestingly just found that this throws an error:

  UsesSetting(
    wrappedValue: <num>(t) => (t.val as num) < 50 ? 0 : null,
  );

But this is fine:

  UsesSetting(
    wrappedValue: <double>(t) => (t.val as num) < 50 ? 0 : null,
  );

And upon further testing, confirmed that the error only occurs when <T> is exactly the same type as as ___.

@eernstg
Copy link
Member

eernstg commented May 4, 2022

Note that <num>(t) => (t.val as num) < 50 ? 0 : null introduces a type variable whose name is num (which is extremely unlikely to be helpful), so you probably meant to write something else (perhaps num was intended to be the name of an existing type, rather than the name that declares a new type variable).

When that function literal is passed to the UsesSetting constructor, it is given a context type of SettingOrFunc? which means int? Function<T>(ContainsValue<T>)?, which is the reason why the actual argument must be a generic function.

Perhaps you meant to write something like this?:

typedef SettingOrFunc<T> = int? Function(ContainsValue<T> tag);

class ContainsValue<T> {
  final T val;
  ContainsValue(this.val);
}

class UsesSetting<T> {
  final SettingOrFunc<T>? setting;
  UsesSetting({this.setting});

  /// Do stuff with wrappedValue here,
  /// for my project, if wrappedValue is/returns null,
  /// default values are used otherwise uses the wrappedValue's return.
}

void main(List<String> args) {
  UsesSetting<num>(
    setting: (t) => t.val == 50 ? 0 : null, // is fine
  );
  UsesSetting<num>(
    setting: (t) => t.val < 50 ? 0 : null,
  );
}

The reported analyzer behavior is basically working as intended: In t.val < 50, t has type ContainsValue<num> (where num is the name of a type variable with no bound), so t.val has a top type (a supertype of all other types), and the only instance members available for such objects are the ones declared by the class Object, and < isn't one of them.

However, it is somewhat surprising that the error message seems to imply that the type of t.val is Object rather than an actual top type (like Object? or dynamic).

@stereotype441, @scheglov, do you think there's a need to adjust this error message?

@eernstg eernstg added area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. improve-diagnostics Related to the quality of diagnostic messages labels May 4, 2022
@stereotype441
Copy link
Member

Hmm, when I try the examples above, I see a slightly different behavior than what was reported:

  • The errors do not come from the analyzer, they come from the front end. The analyzer reports no errors at all (!)
  • In every case I've tried, the error message clearly mentions the top type Object?, not Object. So I don't think the error message needs to be tweaked. @idraper do you have an example where the error message mentions Object rather than Object?? I see your issue mentions Object, but since I only see Object?, I'm assuming that was a typo.

The fact that the front end and the analyzer disagree about whether there is an error is definitely bad. I've opened a separate issue for it: #48955

@eernstg
Copy link
Member

eernstg commented May 4, 2022

Funny, here's a screenshot from DartPad where the analyzer reports an error based on 'Object'
Screen Shot 2022-05-04 at 16 29 04
.

@scheglov
Copy link
Contributor

scheglov commented May 4, 2022

Yes, I also expected for t.val to be resolved to the bound of <num>, i.e. Object?.
This change will switch everything to TypeSystem.resolveToBound(), which is also fixed there.
https://dart-review.googlesource.com/c/sdk/+/243702

I will need to run this through google3 before landing.

And so, we will report:

error: The operator '<' can't be unconditionally invoked because the receiver can be 'null'. (unchecked_use_of_nullable_value at [my_test] bin/test3.dart:22)

@pq pq added the P3 A lower priority bug or feature request label May 4, 2022
@idraper
Copy link
Author

idraper commented May 5, 2022

introduces a type variable whose name is num

Ah - I definitely missed that. This helps me at least understand why the error is being thrown. I thought it was assigning num to the value of T - definitely an oversight.

Perhaps you meant to write something like this?

That was my intended behavior, however, I was specifically trying to avoid having the class generic but as I'm experimenting I'm not finding a way to do it :/. Might just bite the bullet and redesign some other stuff or maybe try something slightly different. Regardless - not related to this issue.

However, it is somewhat surprising that the error message seems to imply that the type of t.val is Object rather than an actual top type (like Object? or dynamic).

Personally, I agree - in hindsight, that's what led to my confusion in the first place.

do you have an example where the error message mentions Object rather than Object?? I see your issue mentions Object, but since I only see Object?, I'm assuming that was a typo.

Checked and confirmed not a typo, same as @eernstg , I only see Object in the message NOT Object?. I also do see the error in the anaylzer (not just visual).

copybara-service bot pushed a commit that referenced this issue May 5, 2022
Bug: #48952
Change-Id: I38add3e0d633f947640283eda46b5399f4559ef8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243702
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
@scheglov scheglov self-assigned this May 5, 2022
@scheglov scheglov closed this as completed May 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. improve-diagnostics Related to the quality of diagnostic messages P3 A lower priority bug or feature request
Projects
None yet
Development

No branches or pull requests

5 participants