Skip to content

RuntimeError: unreachable #623

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

Closed
trusktr opened this issue Jun 1, 2019 · 5 comments
Closed

RuntimeError: unreachable #623

trusktr opened this issue Jun 1, 2019 · 5 comments
Labels

Comments

@trusktr
Copy link
Member

trusktr commented Jun 1, 2019

I keep running into this error, which isn't much help:

Uncaught (in promise) RuntimeError: unreachable
    at wasm-function[19]:41
    at wasm-function[20]:14
    at wasm-function[42]:335
    at wrap (http://127.0.0.1:8080/js/as-loader.js:250:12)
    at runGlas (http://127.0.0.1:8080/ts/index.js:68:9)

And the output of my abort handler:

msg: 0
 file: ~lib/rt/pure.ts
 line: 115
 col: 13

I don't see a pure.ts file here in the repo.

What might cause these errors?

@trusktr
Copy link
Member Author

trusktr commented Jun 1, 2019

I've got some code like the following, which has the error:

code with error
import {Size} from './Size'

function testSize(): boolean {
	let s = new Size(20, 30)

	s = s.add(new Size(40, 50))

	assert(s.width === 60, 's.width is not 60, got ' + s.width.toString())
	assert(s.height === 80, 's.height is not 80, got ' + s.height.toString())

	s.copyFrom(new Size(1, 2))

	assert(s.width === 1, 's.width is not 1, got ' + s.width.toString())
	assert(s.height === 2, 's.height is not 2, got ' + s.height.toString())

	return true
}

export {testSize}

When I comment out two lines, the error goes away:

code without error
import {Size} from './Size'

function testSize(): boolean {
	let s = new Size(20, 30)

	s = s.add(new Size(40, 50))

	// assert(s.width === 60, 's.width is not 60, got ' + s.width.toString())
	// assert(s.height === 80, 's.height is not 80, got ' + s.height.toString())

	s.copyFrom(new Size(1, 2))

	assert(s.width === 1, 's.width is not 1, got ' + s.width.toString())
	assert(s.height === 2, 's.height is not 2, got ' + s.height.toString())

	return true
}

export {testSize}

@trusktr
Copy link
Member Author

trusktr commented Jun 1, 2019

How can I help debug it?

@jtenner
Copy link
Contributor

jtenner commented Jun 1, 2019

  1. Compile using the --debug flag
  2. It looks like a reference is null of some kind. it's a refcounting function that errors (rt/pure.ts)
  3. Use untouched wasm

@MaxGraey
Copy link
Member

MaxGraey commented Jun 1, 2019

Can confirm. This minimal example:

class Foo {
  constructor(public a: i32) {}
  add(other: Foo): Foo { return new Foo(this.a + other.a) }
}

export function test(): void {
  var foo = new Foo(20);
  foo = foo.add(new Foo(40));
  assert(foo.a == 60, foo.a.toString());
}

test();
Full output wat file relate to `test`

 (func $rc/self-assign/test (; 30 ;) (type $FUNCSIG$v)
  (local $0 i32)
  (local $1 i32)
  (local $2 i32)
  i32.const 20
  call $rc/self-assign/Foo#constructor
  local.set $2
  i32.const 40
  call $rc/self-assign/Foo#constructor
  local.tee $1
  call $~lib/rt/pure/__retain
  drop
  local.get $2
  i32.load
  local.get $1
  i32.load
  i32.add
  call $rc/self-assign/Foo#constructor
  local.set $0
  local.get $1
  call $~lib/rt/pure/__release
  local.get $2
  call $~lib/rt/pure/__release
  local.get $0
  i32.load
  i32.const 60
  i32.ne
  if
   local.get $0
   i32.load
   call $~lib/util/number/itoa32
   local.tee $0
   call $~lib/rt/pure/__retain
   local.set $1
   local.get $0
   call $~lib/rt/pure/__release
   local.get $1
   i32.const 296
   i32.const 15
   i32.const 2
   call $~lib/builtins/abort
   unreachable
  end
  local.get $1
  call $~lib/rt/pure/__release
  local.get $0
  call $~lib/rt/pure/__release
  local.get $0
  call $~lib/rt/pure/__release
 )

It seems at the end produce "use after free" issue:

  ...
  local.get $0
  call $~lib/rt/pure/__release
  local.get $0
  call $~lib/rt/pure/__release

@dcodeIO
Copy link
Member

dcodeIO commented Jun 4, 2019

Issue was that the assertion message is not a normal argument, but is only computed if the assertion actually crashes, yet always compiled. This left an uninitialized temporary local around if the assertion succeeded, that just so happened to be a temporary local for one of the Foos intermediary results in another block, then leading to the double release of that one. What the compiler does now is to ignore the message argument GC-wise, as it doesn't matter anymore anyway if the program crashes.

This is very similar to that one paragraph in the runtime README, where I warned about conditionals, just that the compiler didn't even attempt to handle it here 😃

BONUS: Beware of runtime calls in conditional expressions like a ternary IF, logical AND or OR. Each arm can be in either of two states: Either in-flight if immediately retained/returned or not if the expression or the target doesn't support it. Don't fight the compiler there.

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

No branches or pull requests

4 participants