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

Incorrect transformation of mixed boolean expression in unless statement #1384

Open
ghost opened this issue Apr 16, 2019 · 5 comments
Open

Comments

@ghost
Copy link

ghost commented Apr 16, 2019

decaffeinate is producing the wrong JavaScript based on my CoffeeScript input:

func = ->
  return 'condVal' unless cond1 and cond2 or cond3
  'val'

Link to REPL

I get this output:

func = function() {
  if (!(cond1 || !cond2) && !cond3) { return 'condVal'; }
  return 'val';
};

Here's what I expect it to be instead: (Coffee's compiler)

func = function() {
  if (!(cond1 && cond2 || cond3)) { return 'condVal'; }
  return 'val';
};

Or something more similar to decaffeinate's:

func = function() {
  if ((!cond1 || !cond2) && !cond3)) { return 'condVal'; }
  return 'val';
};
@ghost
Copy link
Author

ghost commented Apr 16, 2019

Looks like the bug is in MainStage since the output of NormalizeStage looks okay.

@ghost
Copy link
Author

ghost commented Apr 16, 2019

I get that we're applying DeMorgan's law to the unless statements

!(  cond1 &&  cond2  ||  cond3)
!( (cond1 &&  cond2) ||  cond3) # for clarity (what Coffescript's compiler does)
  !(cond1 &&  cond2) && !cond3) # top level demorgans
  (!cond1 || !cond2) && !cond3) # Maybe this?

But I haven't looked at the code at all, so I have no idea what decaffeinate's actually doing

@eventualbuddha
Copy link
Collaborator

This is the part that triggers it:

if (this.node.isUnless) {
this.condition.negate();
}

Which calls e.g. this:

negate(): void {
this.negated = !this.negated;
this.left.negate();
this.right.negate();
}

@eventualbuddha
Copy link
Collaborator

A note for future me or whoever might try to fix this: the bug is that the ! is inserted before the parenthesis at !(cond1 when it should be inside the parenthesis like (!cond1. This feels more like a string insertion / order of operations bug than a logic error, like both the ( and the ! are inserted at the same position but their order is the reverse of what it should be.

@eventualbuddha
Copy link
Collaborator

The ! is inserted at the wrong time because the call to negate() immediately inserts a ! rather than marking the patcher as negated. This may require a non-trivial refactor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant