Skip to content

Commit 5304dbc

Browse files
authored
Add meta flags to allow redefining Vars and to disable warnings on redef (#292)
* Add meta flags to allow redefining Vars and to disable warnings on redef * Remove unused debug code
1 parent f21e5c9 commit 5304dbc

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

src/basilisp/core/__init__.lpy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@
4646
(fn* with-meta [o meta]
4747
(.with-meta o meta)))
4848

49-
(def ^:macro let
49+
(def ^:macro ^:redef let
5050
(fn* let [&form & decl]
5151
(cons 'let* decl)))
5252

53-
(def ^:macro fn
53+
(def ^:macro ^:redef fn
5454
(fn* fn [&form & decl]
5555
(with-meta
5656
(cons 'fn* decl)
@@ -236,7 +236,7 @@
236236
{:found (first body)})))
237237
(rest body)))]
238238
`(def ~fname
239-
(fn* ~fname
239+
(fn ~fname
240240
~@body)))))
241241

242242
(defn nth

src/basilisp/lang/compiler.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ def _meta_kwargs_ast(ctx: CompilerContext, # pylint:disable=inconsistent-return
433433

434434
_SYM_DYNAMIC_META_KEY = kw.keyword("dynamic")
435435
_SYM_MACRO_META_KEY = kw.keyword("macro")
436+
_SYM_NO_WARN_ON_REDEF_META_KEY = kw.keyword("no-warn-on-redef")
437+
_SYM_REDEF_META_KEY = kw.keyword("redef")
436438

437439

438440
def _is_dynamic(v: Var) -> bool:
@@ -458,6 +460,17 @@ def _is_macro(v: Var) -> bool:
458460
return False
459461

460462

463+
def _is_redefable(v: Var) -> bool:
464+
"""Return True if the Var can be redefined."""
465+
try:
466+
return Maybe(v.meta).map(
467+
lambda m: m.get(_SYM_REDEF_META_KEY, None) # type: ignore
468+
).or_else_get(
469+
False)
470+
except (KeyError, AttributeError):
471+
return False
472+
473+
461474
def _def_ast(ctx: CompilerContext, form: llist.List) -> ASTStream:
462475
"""Return a Python AST Node for a `def` expression."""
463476
assert form.first == _DEF
@@ -486,8 +499,12 @@ def _def_ast(ctx: CompilerContext, form: llist.List) -> ASTStream:
486499
yield from meta_nodes
487500
yield from def_nodes
488501

489-
if safe_name in ctx.current_ns.module.__dict__:
490-
logger.warning(f"redefining local Python name '{safe_name}' in module '{ctx.current_ns.module.__name__}'")
502+
if safe_name in ctx.current_ns.module.__dict__ or form[1] in ctx.current_ns.interns:
503+
no_warn_on_redef = (Maybe(form[1].meta)
504+
.map(lambda m: m.get(_SYM_NO_WARN_ON_REDEF_META_KEY, False)) # type: ignore
505+
.or_else_get(False))
506+
if not no_warn_on_redef:
507+
logger.warning(f"redefining local Python name '{safe_name}' in module '{ctx.current_ns.module.__name__}'")
491508

492509
yield _dependency(ast.Assign(targets=[ast.Name(id=safe_name, ctx=ast.Store())],
493510
value=Maybe(def_value).map(_unwrap_node).or_else_get(ast.NameConstant(None))))
@@ -1487,11 +1504,11 @@ def _kw_ast(_: CompilerContext, form: kw.Keyword) -> ASTStream:
14871504
def _resolve_sym_var(ctx: CompilerContext, v: Var) -> Optional[str]:
14881505
"""Resolve a Basilisp var down to a Python Name (or Attribute).
14891506
1490-
If the Var is marked as :dynamic, do not compile to a direct access.
1491-
If the corresponding function name is not defined in a Python module,
1492-
no direct variable access is possible and Var.find indirection must
1493-
be used."""
1494-
if _is_dynamic(v):
1507+
If the Var is marked as :dynamic or :redef, do not compile to a direct
1508+
access. If the corresponding function name is not defined in a Python
1509+
module, no direct variable access is possible and Var.find indirection
1510+
must be used."""
1511+
if _is_dynamic(v) or _is_redefable(v):
14951512
return None
14961513

14971514
safe_name = munge(v.name.name)

tests/basilisp/compiler_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import uuid
55
from fractions import Fraction
66
from typing import Optional
7+
from unittest import mock
78
from unittest.mock import Mock
89

910
import dateutil.parser as dateparser
@@ -167,6 +168,33 @@ def test_def(ns: runtime.Namespace):
167168
assert lcompile("beep") == "a sound a robot makes"
168169

169170

171+
def test_def_no_warn_on_redef(ns: runtime.Namespace):
172+
with mock.patch('basilisp.lang.compiler.logger') as logger:
173+
lcompile("""
174+
(def unique-zhddkd :a)
175+
(def ^:no-warn-on-redef unique-zhddkd :b)
176+
""")
177+
178+
logger.warning.assert_not_called()
179+
180+
with mock.patch('basilisp.lang.compiler.logger') as logger:
181+
lcompile("""
182+
(def unique-djhvyz :a)
183+
(def unique-djhvyz :b)
184+
""")
185+
186+
logger.warning.assert_called_with(f"redefining local Python name 'unique_djhvyz' in module '{ns.name}'")
187+
188+
189+
def test_redef_vars(ns: runtime.Namespace):
190+
assert kw.keyword('b') == lcompile("""
191+
(def ^:redef orig :a)
192+
(def redef-check (fn* [] orig))
193+
(def orig :b)
194+
(redef-check)
195+
""")
196+
197+
170198
def test_def_dynamic(ns: runtime.Namespace):
171199
v: Var = lcompile("(def ^:dynamic *a-dynamic-var* 1)")
172200
assert v.dynamic is True

0 commit comments

Comments
 (0)