frontend: simplify uses of comparison operators#466
Conversation
This is preparatory work to changing the IR of comparison
operators. Just making sure that non-overridden operators,
especially the Int32 variants, are directly available.
Also, instead of using the `==` and `!=` cover functions
in other desugarings, use simpler tests like `isNull`.
This required changing frontend and backend test goldens
so I thought I'd get it out of the way first.
Specific changes:
- Coalescing operator, `??`, now uses `isNull` in its desugaring, not `!= null`.
- Comparison operators like `<` are no longer null accepting.
be-rust no longer wraps f64 as options to compare them but
`==` and `!=` still are.
- CoverFunction.uncover is now a static method so users like `new` and
DotHelper no longer have to create CoverFunctions under the hood.
- Hardcoded ASTs in tests like ControlFlowTest use specific comparison
operators, not the cover function values.
This is a draft. There is a failure in be-rust. Functional tests that depend on std, like typesDate fail.
The use of `.cmp` below is flagged as an error.
```rs
fn decodeHexUnsigned__361(sourceText__708: impl temper_core::ToArcString, start__709: usize, limit__710: usize) -> i32 {
let sourceText__708 = sourceText__708.to_arc_string();
let return__307: i32;
let mut t___1642: bool;
let mut t___1644: bool;
let mut t___1647: bool;
let mut t___1650: i32;
'fn__711: {
let mut n__712: i32 = 0;
let mut i__713: usize = start__709;
'loop___2712: loop {
if ! (Some(i__713).cmp( & Some(limit__710)) as i32 < 0) {
break;
}
```
It corresponds to code from std/json:
```ts
let decodeHexUnsigned(
sourceText: String, start: StringIndex, limit: StringIndex
): Int {
var n = 0;
var i = start;
while (i.compareTo(limit) < 0) {
```
Signed-off-by: Mike Samuel <mikesamuel@gmail.com>
| translator: RustTranslator, | ||
| ): Rust.Expr { | ||
| val cmpFn = when (operator) { | ||
| RustOperator.Equals, RustOperator.NotEquals -> "cmp_option" |
There was a problem hiding this comment.
I want == and != for ints to eventually be non-null accepting, but for now, they still are.
| BuiltinOperatorId.LtIntInt, | ||
| ) { cmp -> | ||
| TBoolean.value(cmp < 0) | ||
| } |
There was a problem hiding this comment.
Exposing these out so that the non-overloaded version is readily available.
This will make it easier to desugar overloaded comparisons to a use of <=> nested inside a guaranteed int comparison of its output.
| { | ||
| Call { | ||
| V(Value(BuiltinFuns.notEqualsFn)) | ||
| V(vIsNullFn) |
There was a problem hiding this comment.
One fewer use of a cover function.
| ("..." to OutputTokenType.Name) to false, // ...Int | ||
| (OutputTokenType.Word to "(") to true, // if ( | ||
| (OutputTokenType.Name to "{") to true, // f { | ||
| (OutputTokenType.NumericValue to "{") to true, // if x == 0 { |
There was a problem hiding this comment.
Rust was formatting oddly: if x < 0{. Now there's a space before the bracket.
| while (i.compareTo(limit) < 0) { | ||
| let cp = sourceText[i]; | ||
| let digit = if (char'0' <= cp && cp <= char'0') { | ||
| let digit = if (char'0' <= cp && cp <= char'9') { |
| accessibleMembers.isEmpty() && extensions.isNotEmpty() && | ||
| extensions.all { (it as? FunctionResolution)?.fn is CallableValue } | ||
| ) { | ||
| val cover = CoverFunction(extensions.map { (it as FunctionResolution).fn as CallableValue }) |
There was a problem hiding this comment.
Fewer creations of cover functions under the hood.
| TODO("Link to a macro environment so we can evaluate the value tree") | ||
| val (_, newValue) = newValues[index] | ||
| ?: TODO("Link to a macro environment so we can evaluate the value tree $index, $interpMode") | ||
| newValue.valueContained ?: Fail |
There was a problem hiding this comment.
new now cherry picks arguments to pass and the this argument reaches this path.
| cb: InterpreterCallback, | ||
| interpMode: InterpMode, | ||
| covered: List<CallableValue>, | ||
| otherwise: CallableValue? = null, |
There was a problem hiding this comment.
Only change from above is these two arguments passed in instead of being accessed as fields, and a change to the recursive call at L129.
Signed-off-by: Tom <tom@temper.systems>
Signed-off-by: Tom <tom@temper.systems>
| | let mut i__0: usize = start__0; | ||
| | 'loop___0: loop { | ||
| | if ! (Some(Some(i__0).cmp( & Some(limit__0)) as i32) < Some(0)) { | ||
| | if ! (Some(i__0).cmp( & Some(limit__0)) as i32 < 0) { |
There was a problem hiding this comment.
Looks like the outer Some(...) was masking the now exposed as i32 < 0.
Address this error:
```rs
--> C:\Users\work2\AppData\Local\Temp\temper-test3896171247360590248\rust\std\src\json\mod.rs:1782:64
|
1782 | if ! (Some(i__713).cmp( & Some(limit__710)) as i32 < 0) {
| ^ -- interpreted as generic arguments
| |
| not interpreted as comparison
|
help: try comparing the cast value
|
1782 | if ! ((Some(i__713).cmp( & Some(limit__710)) as i32) < 0) {
| + +
```
Signed-off-by: Mike Samuel <mikesamuel@gmail.com>
| |pub fn hi(n__0: i32) { | ||
| | let mut i__0: i32 = 0; | ||
| | 'loop___0: while Some(i__0) < Some(n__0) { | ||
| | 'loop___0: while i__0 < n__0 { |
There was a problem hiding this comment.
Yeah. Comparisons were just doing too much. Uses of non-nullable types shouldn't pay for null-safety.
This is preparatory work to changing the IR of comparison operators. Just making sure that non-overridden operators, especially the Int32 variants, are directly available.
Also, instead of using the
==and!=cover functions in other desugarings, use simpler tests likeisNull.This required changing frontend and backend test goldens so I thought I'd get it out of the way first.
Specific changes:
??, now usesisNullin its desugaring, not!= null.<are no longer null accepting. be-rust no longer wraps f64 as options to compare them but==and!=still are.newand DotHelper no longer have to create CoverFunctions under the hood.