// non-null values are subtype of null values
wasmValidateText(`(module
  (func (param $a (ref extern))
    local.get $a
    (block (param (ref null extern))
      drop
    )
  )
)`);

// null values are not subtype of non-null values
wasmFailValidateText(`(module
  (func (param $a (ref null extern))
    local.get $a
    (block (param (ref extern))
      drop
    )
  )
)`, /expression has type externref but expected \(ref extern\)/);

// can have non-defaultable local, but not use/get if unset.
wasmValidateText(`(module
  (func (local (ref extern)))
)`);
wasmFailValidateText(`(module
  (func (local (ref extern))
    local.get 0
    drop
  )
)`, /local\.get read from unset local/);
wasmFailValidateText(`(module
  (func
    (local (ref extern))
    unreachable
    block
      local.get 0
      drop
    end
  )
)`, /local\.get read from unset local/);
wasmFailValidateText(`(module
  (func (param funcref) (result funcref) (local (ref func))
    block
      local.get 0
      ref.as_non_null
      local.set 1
    end
    local.get 1
  )
)`, /local\.get read from unset local/);
wasmValidateText(`(module
  (func (param $r (ref extern))
    (local $var (ref extern))
    local.get $r
    ref.as_non_null
    local.set $var
    block block block
    local.get $var
    drop
    end end end
  )
  (func
    (param (ref null func) (ref null func) (ref func))
    (result funcref)
    (local (ref func) i32 (ref func) (ref null func))
    local.get 0
    ref.as_non_null
    local.tee 3
    block
      local.get 6
      ref.as_non_null
      local.set 5 
    end
    local.get 2
    drop
    local.tee 5
  )  
)`);
wasmFailValidateText(`(module
  (elem declare func 0)
  (func
    (local (ref func))
    i32.const 0
    if
      ref.func 0
      local.set 0
    else
      local.get 0
      drop
    end
  )
)`, /local\.get read from unset local/);
wasmValidateText(`(module
  (elem declare func 0)
  (func (result funcref)
    (local (ref func) (ref func))
    i32.const 0
    if (result funcref)
      ref.func 0
      local.set 0
      local.get 0
    else
      ref.func 0
      local.tee 1
      local.get 1
      drop
    end
  )
)`);

// exported funcs can't take null in non-nullable params
let {a} = wasmEvalText(`(module
  (func (export "a") (param (ref extern)))
)`).exports;
assertErrorMessage(() => a(null), TypeError, /cannot pass null to non-nullable/);
for (let val of WasmNonNullExternrefValues) {
  a(val);
}

// imported funcs can't return null in non-nullable results
function returnNull() {
  return null;
}
function returnMultiNullReg() {
  return [null, null];
}
function returnMultiNullStack() {
  return [1, 2, 3, 4, 5, 6, 7, 8, null];
}
let {runNull, runMultiNullReg, runMultiNullStack} = wasmEvalText(`(module
  (func $returnNull (import "" "returnNull") (result (ref extern)))
  (func $returnMultiNullReg (import "" "returnMultiNullReg") (result (ref extern) (ref extern)))
  (func $returnMultiNullStack (import "" "returnMultiNullStack") (result (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern) (ref extern)))
  (func (export "runNull")
    call $returnNull
    unreachable
  )
  (func (export "runMultiNullReg")
    call $returnMultiNullReg
    unreachable
  )
  (func (export "runMultiNullStack")
    call $returnMultiNullStack
    unreachable
  )
)`, { "": { returnNull, returnMultiNullReg, returnMultiNullStack } }).exports;
assertErrorMessage(() => runNull(), TypeError, /cannot pass null to non-nullable/);
assertErrorMessage(() => runMultiNullReg(), TypeError, /cannot pass null to non-nullable/);
assertErrorMessage(() => runMultiNullStack(), TypeError, /cannot pass null to non-nullable/);

{
  // can have non-nullable globals
  wasmEvalText(`(module
    (func $f)
    (elem declare func $f)
    (global (ref func) ref.func $f)
  )`);
}

// cannot have non-nullable tables without initializer
wasmFailValidateText(`(module
  (table (ref extern) (elem))
)`, /table with non-nullable references requires initializer/);

// Testing internal wasmLosslessInvoke to pass non-nullable as params and arguments.
let {t} = wasmEvalText(`(module
  (func (export "t") (param (ref extern)) (result (ref extern))
    (local (ref extern))
    (local.set 1 (local.get 0))
    (local.get 1)
  )
)`).exports;
const ret = wasmLosslessInvoke(t, {test: 1});
assertEq(ret.value.test, 1);
