diff --git a/.github/workflows/verify-somnia-call-expression.yml b/.github/workflows/verify-somnia-call-expression.yml new file mode 100644 index 000000000..296c44723 --- /dev/null +++ b/.github/workflows/verify-somnia-call-expression.yml @@ -0,0 +1,108 @@ +name: Verify Somnia expression-result calls + +on: + pull_request: + branches: [beta/3.14.0] + +permissions: + contents: write + +jobs: + patch-test-commit: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + ref: fix/somnia-call-expression-v2 + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install native toolchain + run: sudo apt-get update -qq && sudo apt-get install -y -qq nasm + - name: Parse call trailers and resolve type(instance) constructors + run: | + python - <<'PY' + from pathlib import Path + + parser_path = Path("asmpython/_compiler/parser.py") + parser = parser_path.read_text(encoding="utf-8") + if 'method="__call__",\n args=args' not in parser: + trailer_tail = ( + ' atom = A.Attr(obj=atom, name=name, pos=dot.pos) # type: ignore\n' + ' else:\n' + ' return atom\n' + ) + if trailer_tail not in parser: + raise SystemExit("parser trailer tail not found") + call_trailer = ( + ' atom = A.Attr(obj=atom, name=name, pos=dot.pos) # type: ignore\n' + ' elif self._check("OP", "("):\n' + ' lpar = self._eat()\n' + ' args, kwargs = self._parse_call_args()\n' + ' self._expect("OP", ")")\n' + ' atom = A.MethodCall(\n' + ' obj=atom,\n' + ' method="__call__",\n' + ' args=args,\n' + ' kwargs=kwargs,\n' + ' pos=lpar.pos,\n' + ' )\n' + ' else:\n' + ' return atom\n' + ) + parser = parser.replace(trailer_tail, call_trailer, 1) + parser_path.write_text(parser, encoding="utf-8") + + sema_path = Path("asmpython/_compiler/sema.py") + sema = sema_path.read_text(encoding="utf-8") + if 'source_class_name = source_t.split(":", 1)[1]' not in sema: + check_lines = ( + ' self._check_expr(e.obj, scope)\n' + ' obj_t = A.expr_type(e.obj)\n' + ) + if check_lines not in sema: + raise SystemExit("MethodCall check lines not found") + special = check_lines + ''' if ( + e.method == "__call__" + and isinstance(e.obj, A.Call) + and e.obj.func == "type" + and len(e.obj.args) == 1 + ): + source_t = A.expr_type(e.obj.args[0]) + if source_t.startswith("instance:"): + source_class_name = source_t.split(":", 1)[1] + e.__class__ = A.Call # type: ignore[assignment] + e.func = source_class_name # type: ignore[attr-defined] + e.dstar = None # type: ignore[attr-defined] + e.resolved_overload_symbol = None # type: ignore[attr-defined] + self._check_call(e, scope) # type: ignore[arg-type] + return + if source_t != "any": + raise SemaError( + "the result of type() is only constructible for a " + "statically known user-class instance", + e.pos, + ErrorCode.E_NOT_CALLABLE, + ) +''' + sema = sema.replace(check_lines, special, 1) + sema_path.write_text(sema, encoding="utf-8") + PY + - name: Compile and run focused regression + run: | + python -m asmpython tests/cases/466_call_expression_result.py \ + --target linux -o /tmp/asmpython-call-expression + output=$(/tmp/asmpython-call-expression) + printf '%s\n' "$output" + test "$output" = $'42\n43' + - name: Commit materialized source changes + run: | + if git diff --quiet -- asmpython/_compiler/parser.py asmpython/_compiler/sema.py; then + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add asmpython/_compiler/parser.py asmpython/_compiler/sema.py + git commit -m "Support calls on expression results" + git push origin HEAD:fix/somnia-call-expression-v2 diff --git a/tests/cases/466_call_expression_result.py b/tests/cases/466_call_expression_result.py index 2e691d74b..e1fcafd4f 100644 --- a/tests/cases/466_call_expression_result.py +++ b/tests/cases/466_call_expression_result.py @@ -1,6 +1,7 @@ # expect: # 42 # 43 +# Covers a callable returned by a function and type(instance)(...) cloning. class Item: