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

swc explicitResourceManagement SuppressedError implement does not obey spec #8853

Open
loynoir opened this issue Apr 12, 2024 · 8 comments · Fixed by #8860
Open

swc explicitResourceManagement SuppressedError implement does not obey spec #8853

loynoir opened this issue Apr 12, 2024 · 8 comments · Fixed by #8860
Assignees
Labels
Milestone

Comments

@loynoir
Copy link

loynoir commented Apr 12, 2024

Describe the bug

swc explicitResourceManagement SuppressedError implement does not obey spec.

swc runtime output is different from tsc runtime output.

Input code

import { deepStrictEqual } from 'node:assert'
import process from 'node:process'

let i = 0
let err: any
try {
  await using _x1 = {
    async [Symbol.asyncDispose]() {
      throw [1, ++i]
    }
  }

  await using _x2 = {
    async [Symbol.asyncDispose]() {
      throw [2, ++i]
    }
  }

  await using _x3 = {
    async [Symbol.asyncDispose]() {
      throw [3, ++i]
    }
  }

  await using _x4 = {
    async [Symbol.asyncDispose]() {
      throw [4, ++i]
    }
  }

  throw [5, ++i]
} catch (e) {
  err = e
}

if (process.env['REPRODUCE_SWC_BUG'] === undefined) {
  deepStrictEqual(err.error, [1, 5])
  deepStrictEqual(err.suppressed.error, [2, 4])
  deepStrictEqual(err.suppressed.suppressed.error, [3, 3])
  deepStrictEqual(err.suppressed.suppressed.suppressed.error, [4, 2])
  deepStrictEqual(err.suppressed.suppressed.suppressed.suppressed, [5, 1])
} else {
  deepStrictEqual(err.suppressed, [1, 5])
  deepStrictEqual(err.error.suppressed, [2, 4])
  deepStrictEqual(err.error.error.suppressed, [3, 3])
  deepStrictEqual(err.error.error.error.suppressed, [4, 2])
  deepStrictEqual(err.error.error.error.error, [5, 1])
}

Config

{
    "$schema": "https://json.schemastore.org/swcrc",
    "jsc": {
        "parser": {
            "syntax": "typescript",
            "explicitResourceManagement": true,
        },
        "target": "es2022",
        "loose": false,
        "minify": {
            "compress": false,
            "mangle": false
        }
    },
    "module": {
        "type": "es6"
    },
    "minify": false,
    "isModule": true
}

Playground link (or link to the minimal reproduction)

#8853

SWC Info output

No response

Expected behavior

  • If tsc is correct, expect same output as tsc.

Actual behavior

$ yarn tsc --build
...
$ yarn swc ./reproduce.mts -o reproduce/reproduce.swc.mjs
...
$ node ./reproduce/reproduce.tsc.mjs && REPRODUCE_SWC_BUG=1 node ./reproduce/reproduce.swc.mjs && echo OK
OK

Version

0.3.12

Additional context

@swc/cli: 0.3.12
@swc/core: 1.4.13

Downstream

denoland/deno#23249

@kdy1
Copy link
Member

kdy1 commented Apr 15, 2024

Babel output:

import { deepStrictEqual } from 'node:assert';
function _usingCtx2() { var r = "function" == typeof SuppressedError ? SuppressedError : function (r, n) { var e = Error(); return e.name = "SuppressedError", e.suppressed = n, e.error = r, e; }, n = {}, e = []; function using(r, n) { if (null != n) { if (Object(n) !== n) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined."); if (r) var o = n[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]; if (null == o && (o = n[Symbol.dispose || Symbol.for("Symbol.dispose")]), "function" != typeof o) throw new TypeError("Property [Symbol.dispose] is not a function."); e.push({ v: n, d: o, a: r }); } return n; } return { e: n, u: using.bind(null, !1), a: using.bind(null, !0), d: function () { var o = this.e; function next() { for (; r = e.pop();) try { var r, t = r.d.call(r.v); if (r.a) return Promise.resolve(t).then(next, err); } catch (r) { return err(r); } if (o !== n) throw o; } function err(e) { return o = o !== n ? new r(o, e) : e, next(); } return next(); } }; }
let i = 0;
let err;
try {
    try {
        var _usingCtx = _usingCtx2();
        const _x1 = _usingCtx.a({
            async [Symbol.asyncDispose]() {
                throw [1, ++i];
            }
        });
        const _x2 = _usingCtx.a({
            async [Symbol.asyncDispose]() {
                throw [2, ++i];
            }
        });
        const _x3 = _usingCtx.a({
            async [Symbol.asyncDispose]() {
                throw [3, ++i];
            }
        });
        const _x4 = _usingCtx.a({
            async [Symbol.asyncDispose]() {
                throw [4, ++i];
            }
        });
        throw [5, ++i];
    } catch (_) {
        _usingCtx.e = _;
    } finally {
        await _usingCtx.d();
    }
} catch (e) {
    err = e;
}
deepStrictEqual(err.error, 1);
deepStrictEqual(err.suppressed.error, 2);
deepStrictEqual(err.suppressed.suppressed.error, 3);
deepStrictEqual(err.suppressed.suppressed.suppressed.error, 4);
deepStrictEqual(err.suppressed.suppressed.suppressed.suppressed, 5);

kdy1 added a commit that referenced this issue Apr 15, 2024
@kdy1 kdy1 modified the milestones: Planned, v1.4.15 Apr 17, 2024
@loynoir
Copy link
Author

loynoir commented Apr 18, 2024

@kdy1

$ yarn tsc --version \
    && yarn swc --version \
    && yarn tsc --build \
    && yarn swc ./reproduce.mts -o ./swc/reproduce.mjs \
    && node ./tsc/reproduce.mjs \
    && node ./swc/reproduce.mjs \
    && echo OK
Version 5.4.5

@swc/cli: 0.3.12
@swc/core: 1.4.15

Successfully compiled 1 file with swc.
node:internal/process/esm_loader:34
      internalBinding('errors').triggerUncaughtException(
                                ^

AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected

+ undefined
- [
-   1,
-   5
- ]
    at file:///path/to/swc/reproduce.mjs:115:5
    at ModuleJob.run (node:internal/modules/esm/module_job:222:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:323:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:120:12) {
  generatedMessage: true,
  code: 'ERR_ASSERTION',
  actual: undefined,
  expected: [ 1, 5 ],
  operator: 'deepStrictEqual'
}

Node.js v21.7.1
import { deepStrictEqual } from 'node:assert'
import process from 'node:process'

let i = 0
let err: any
try {
  await using _x1 = {
    async [Symbol.asyncDispose]() {
      throw [1, ++i]
    }
  }

  await using _x2 = {
    async [Symbol.asyncDispose]() {
      throw [2, ++i]
    }
  }

  await using _x3 = {
    async [Symbol.asyncDispose]() {
      throw [3, ++i]
    }
  }

  await using _x4 = {
    async [Symbol.asyncDispose]() {
      throw [4, ++i]
    }
  }

  throw [5, ++i]
} catch (e) {
  err = e
}

if (process.env['REPRODUCE_SWC_BUG'] === undefined) {
  deepStrictEqual(err.error, [1, 5])
  deepStrictEqual(err.suppressed.error, [2, 4])
  deepStrictEqual(err.suppressed.suppressed.error, [3, 3])
  deepStrictEqual(err.suppressed.suppressed.suppressed.error, [4, 2])
  deepStrictEqual(err.suppressed.suppressed.suppressed.suppressed, [5, 1])
} else {
  deepStrictEqual(err.suppressed, [1, 5])
  deepStrictEqual(err.error.suppressed, [2, 4])
  deepStrictEqual(err.error.error.suppressed, [3, 3])
  deepStrictEqual(err.error.error.error.suppressed, [4, 2])
  deepStrictEqual(err.error.error.error.error, [5, 1])
}

@loynoir
Copy link
Author

loynoir commented Apr 18, 2024

Actually, swc core 1.4.15 err is ReferenceError: resource is not defined.

And I found babel also have different output with tsc.

@kdy1
Copy link
Member

kdy1 commented Apr 18, 2024

#8872

@kdy1
Copy link
Member

kdy1 commented Apr 18, 2024

In my experience, when babel vs tsc is different, tsc was the problem, but need to check

@loynoir
Copy link
Author

loynoir commented Apr 18, 2024

I reverted to 1.4.14, and opened an issue at typescript.

@loynoir
Copy link
Author

loynoir commented Apr 18, 2024

@kdy1

TypeScript is the correct one here.

@loynoir
Copy link
Author

loynoir commented Apr 18, 2024

import { deepStrictEqual } from 'node:assert'
import process from 'node:process'

let i = 0
/** @type {any} */
let err
try {
  await using _x1 = {
    async [Symbol.asyncDispose]() {
      throw [1, ++i]
    }
  }

  await using _x2 = {
    async [Symbol.asyncDispose]() {
      throw [2, ++i]
    }
  }

  await using _x3 = {
    async [Symbol.asyncDispose]() {
      throw [3, ++i]
    }
  }

  await using _x4 = {
    async [Symbol.asyncDispose]() {
      throw [4, ++i]
    }
  }

  throw [5, ++i]
} catch (e) {
  err = e
}

switch (process.env['TRANSPILER']) {
  case 'tsc':
    deepStrictEqual(err.error, [1, 5])
    deepStrictEqual(err.suppressed.error, [2, 4])
    deepStrictEqual(err.suppressed.suppressed.error, [3, 3])
    deepStrictEqual(err.suppressed.suppressed.suppressed.error, [4, 2])
    deepStrictEqual(err.suppressed.suppressed.suppressed.suppressed, [5, 1])
    break
  case 'swc':
    deepStrictEqual(err.suppressed, [1, 5])
    deepStrictEqual(err.error.suppressed, [2, 4])
    deepStrictEqual(err.error.error.suppressed, [3, 3])
    deepStrictEqual(err.error.error.error.suppressed, [4, 2])
    deepStrictEqual(err.error.error.error.error, [5, 1])
    break
  case 'babel':
    deepStrictEqual(err.suppressed, [1, 5])
    deepStrictEqual(err.error.suppressed, [2, 4])
    deepStrictEqual(err.error.error.suppressed, [3, 3])
    deepStrictEqual(err.error.error.error.suppressed, [4, 2])
    deepStrictEqual(err.error.error.error.error, [5, 1])
    break
  default:
    // break
    throw new Error()
}

// export { err }

@kdy1 kdy1 reopened this Apr 18, 2024
@kdy1 kdy1 modified the milestones: v1.4.15, Planned Apr 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

2 participants