Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
### Changed
* Single arity functions can be tagged with `^:allow-unsafe-names` to preserve their parameter names (#1212)
* Single arity functions can be tagged with `^:allow-unsafe-names` to preserve their parameter names (#1212)

### Fixed
* Fix an issue where the compiler would generate an illegal `return` statement for asynchronous generators (#1180)

## [v0.3.7]
### Fixed
Expand Down
133 changes: 78 additions & 55 deletions src/basilisp/lang/compiler/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@
import sys
import uuid
from collections import defaultdict
from collections.abc import Collection, Iterable, Mapping, MutableMapping, MutableSet
from collections.abc import (
Collection,
Iterable,
Iterator,
Mapping,
MutableMapping,
MutableSet,
)
from datetime import datetime
from decimal import Decimal
from fractions import Fraction
Expand Down Expand Up @@ -89,6 +96,7 @@
Fn,
FnArity,
FunctionContext,
FunctionContextType,
HostCall,
HostField,
If,
Expand Down Expand Up @@ -447,16 +455,23 @@ def is_async_ctx(self) -> bool:

It is possible that the current function is defined inside other functions,
so this does not imply anything about the nesting level of the current node."""
return self.func_ctx == FunctionContext.ASYNC_FUNCTION
func_ctx = self.func_ctx
return (
func_ctx is not None
and func_ctx.function_type == FunctionContextType.ASYNC_FUNCTION
)

@contextlib.contextmanager
def new_func_ctx(self, context_type: FunctionContext):
def new_func_ctx(
self, context_type: FunctionContextType
) -> Iterator[FunctionContext]:
"""Context manager which can be used to set a function or method context for
child nodes to examine. A new function context is pushed onto the stack each
time the Analyzer finds a new function or method definition, so there may be
many nested function contexts."""
self._func_ctx.append(context_type)
yield
func_ctx = FunctionContext(context_type)
self._func_ctx.append(func_ctx)
yield func_ctx
self._func_ctx.pop()

@property
Expand Down Expand Up @@ -1186,7 +1201,7 @@ def __deftype_method_param_bindings(
return has_vargs, fixed_arity, param_nodes


def __deftype_classmethod(
def __deftype_classmethod( # pylint: disable=too-many-locals
form: Union[llist.PersistentList, ISeq],
ctx: AnalyzerContext,
method_name: str,
Expand Down Expand Up @@ -1222,24 +1237,25 @@ def __deftype_classmethod(
has_vargs, fixed_arity, param_nodes = __deftype_method_param_bindings(
params, ctx, SpecialForm.DEFTYPE
)
with ctx.new_func_ctx(FunctionContext.CLASSMETHOD), ctx.expr_pos():
with ctx.new_func_ctx(FunctionContextType.CLASSMETHOD), ctx.expr_pos():
stmts, ret = _body_ast(runtime.nthrest(form, 2), ctx)
method = DefTypeClassMethod(
form=form,
name=method_name,
params=vec.vector(param_nodes),
fixed_arity=fixed_arity,
is_variadic=has_vargs,
kwarg_support=kwarg_support,
body=Do(
body = Do(
form=form.rest,
statements=vec.vector(stmts),
ret=ret,
is_body=True,
# Use the argument vector or first body statement, whichever
# exists, for metadata.
env=ctx.get_node_env(),
),
)
method = DefTypeClassMethod(
form=form,
name=method_name,
params=vec.vector(param_nodes),
fixed_arity=fixed_arity,
is_variadic=has_vargs,
kwarg_support=kwarg_support,
body=body,
class_local=cls_binding,
env=ctx.get_node_env(),
)
Expand Down Expand Up @@ -1286,8 +1302,17 @@ def __deftype_or_reify_method( # pylint: disable=too-many-arguments,too-many-lo

loop_id = genname(method_name)
with ctx.new_recur_point(loop_id, param_nodes):
with ctx.new_func_ctx(FunctionContext.METHOD), ctx.expr_pos():
with ctx.new_func_ctx(FunctionContextType.METHOD), ctx.expr_pos():
stmts, ret = _body_ast(runtime.nthrest(form, 2), ctx)
body = Do(
form=form.rest,
statements=vec.vector(stmts),
ret=ret,
is_body=True,
# Use the argument vector or first body statement, whichever
# exists, for metadata.
env=ctx.get_node_env(),
)
method = DefTypeMethodArity(
form=form,
name=method_name,
Expand All @@ -1296,15 +1321,7 @@ def __deftype_or_reify_method( # pylint: disable=too-many-arguments,too-many-lo
fixed_arity=fixed_arity,
is_variadic=has_vargs,
kwarg_support=kwarg_support,
body=Do(
form=form.rest,
statements=vec.vector(stmts),
ret=ret,
is_body=True,
# Use the argument vector or first body statement, whichever
# exists, for metadata.
env=ctx.get_node_env(),
),
body=body,
loop_id=loop_id,
env=ctx.get_node_env(),
)
Expand Down Expand Up @@ -1356,22 +1373,23 @@ def __deftype_or_reify_property(

assert not has_vargs, f"{special_form} properties may not have arguments"

with ctx.new_func_ctx(FunctionContext.PROPERTY), ctx.expr_pos():
with ctx.new_func_ctx(FunctionContextType.PROPERTY), ctx.expr_pos():
stmts, ret = _body_ast(runtime.nthrest(form, 2), ctx)
prop = DefTypeProperty(
form=form,
name=method_name,
this_local=this_binding,
params=vec.vector(param_nodes),
body=Do(
body = Do(
form=form.rest,
statements=vec.vector(stmts),
ret=ret,
is_body=True,
# Use the argument vector or first body statement, whichever
# exists, for metadata.
env=ctx.get_node_env(),
),
)
prop = DefTypeProperty(
form=form,
name=method_name,
this_local=this_binding,
params=vec.vector(param_nodes),
body=body,
env=ctx.get_node_env(),
)
prop.visit(partial(_assert_no_recur, ctx))
Expand All @@ -1393,24 +1411,25 @@ def __deftype_staticmethod(
has_vargs, fixed_arity, param_nodes = __deftype_method_param_bindings(
args, ctx, SpecialForm.DEFTYPE
)
with ctx.new_func_ctx(FunctionContext.STATICMETHOD), ctx.expr_pos():
with ctx.new_func_ctx(FunctionContextType.STATICMETHOD), ctx.expr_pos():
stmts, ret = _body_ast(runtime.nthrest(form, 2), ctx)
method = DefTypeStaticMethod(
form=form,
name=method_name,
params=vec.vector(param_nodes),
fixed_arity=fixed_arity,
is_variadic=has_vargs,
kwarg_support=kwarg_support,
body=Do(
body = Do(
form=form.rest,
statements=vec.vector(stmts),
ret=ret,
is_body=True,
# Use the argument vector or first body statement, whichever
# exists, for metadata.
env=ctx.get_node_env(),
),
)
method = DefTypeStaticMethod(
form=form,
name=method_name,
params=vec.vector(param_nodes),
fixed_arity=fixed_arity,
is_variadic=has_vargs,
kwarg_support=kwarg_support,
body=body,
env=ctx.get_node_env(),
)
method.visit(partial(_assert_no_recur, ctx))
Expand Down Expand Up @@ -2092,29 +2111,30 @@ def __fn_method_ast( # pylint: disable=too-many-locals
with ctx.new_recur_point(fn_loop_id, param_nodes):
with (
ctx.new_func_ctx(
FunctionContext.ASYNC_FUNCTION
FunctionContextType.ASYNC_FUNCTION
if is_async
else FunctionContext.FUNCTION
else FunctionContextType.FUNCTION
),
ctx.expr_pos(),
):
stmts, ret = _body_ast(form.rest, ctx)
method = FnArity(
form=form,
loop_id=fn_loop_id,
params=vec.vector(param_nodes),
tag=return_tag,
is_variadic=has_vargs,
fixed_arity=len(param_nodes) - int(has_vargs),
body=Do(
body = Do(
form=form.rest,
statements=vec.vector(stmts),
ret=ret,
is_body=True,
# Use the argument vector or first body statement, whichever
# exists, for metadata.
env=ctx.get_node_env(),
),
)
method = FnArity(
form=form,
loop_id=fn_loop_id,
params=vec.vector(param_nodes),
tag=return_tag,
is_variadic=has_vargs,
fixed_arity=len(param_nodes) - int(has_vargs),
body=body,
# Use the argument vector for fetching line/col since the
# form itself is a sequence with no meaningful metadata.
env=ctx.get_node_env(),
Expand Down Expand Up @@ -3420,6 +3440,9 @@ def _yield_ast(form: ISeq, ctx: AnalyzerContext) -> Yield:
"yield forms must contain 1 or 2 elements, as in: (yield [expr])", form=form
)

# Indicate that the current function is a generator
ctx.func_ctx.is_generator = True

if nelems == 2:
with ctx.expr_pos():
expr = _analyze_form(runtime.nth(form, 1), ctx)
Expand Down
12 changes: 11 additions & 1 deletion src/basilisp/lang/compiler/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
Do,
Fn,
FnArity,
FunctionContextType,
HostCall,
HostField,
If,
Expand Down Expand Up @@ -1715,7 +1716,16 @@ def __fn_args_to_py_ast(

body_ast = _synthetic_do_to_py_ast(ctx, body)
fn_body_ast.extend(map(statementize, body_ast.dependencies))
fn_body_ast.append(ast.Return(value=body_ast.node))

func_ctx = body.env.func_ctx
if (
func_ctx is not None
and func_ctx.is_generator
and func_ctx.function_type == FunctionContextType.ASYNC_FUNCTION
):
fn_body_ast.append(ast.Pass())
else:
fn_body_ast.append(ast.Return(value=body_ast.node))

return fn_args, varg, fn_body_ast, fn_def_deps

Expand Down
Loading