Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
* Added a compiler metadata flag for suppressing warnings when Var indirection is unavoidable (#1052)

## [v0.2.2]
### Added
Expand Down
4 changes: 4 additions & 0 deletions docs/compiler.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ The ``^:dynamic`` metadata key will force all accesses to the so-marked Var to b
The ``^:redef`` metadata key can be used if you intend to re-``def`` a Var later and you need changes to be propagated.
It is unlikely you will want to do this, but you can configure the compiler to emit all Var accesses with indirection using the ``use-var-indirection`` configuration option in :ref:`compiler_generation_configuration`.

There may be cases where direct linking is impossible, such as in Var references generated within macros when the compiler cannot verify the existence of the referenced name.
For such cases, the compiler will emit a warning if a direct link is not possible (if it is so configured).
You can suppress those warnings locally by attaching the ``^:no-warn-on-var-indirection`` metadata to the symbol in question.

.. note::

Changes to Vars which were direct linked will not be propagated to any code that used the direct link, rather than Var indirection.
Expand Down
27 changes: 14 additions & 13 deletions src/basilisp/core.lpy
Original file line number Diff line number Diff line change
Expand Up @@ -6203,7 +6203,7 @@
:object-type obj-type#})))))
arglists))))
(let [dotted-method-name (symbol (str "." (name method-name)))]
`(.register ~method-name
`(.register ~(vary-meta method-name assoc :no-warn-on-var-indirection true)
~interface-name
(fn ~method-name
~@(map (fn [[obj-sym & args]]
Expand Down Expand Up @@ -6281,7 +6281,7 @@
:interface ~interface-sym
:methods ~(apply hash-map
(mapcat #(let [v (first %)]
[(keyword (name v)) v])
[(keyword (name v)) (vary-meta v assoc :no-warn-on-var-indirection true)])
methods))
:var (var ~protocol-name)}
(alter-var-root (var ~protocol-name) merge ~protocol-name)))
Expand Down Expand Up @@ -7091,14 +7091,15 @@
;;;;;;;;;;;;;;;;;;;;;;;;;

(defmulti ^:private make-custom-data-readers
(fn [obj metadata] (type obj)))
(fn [obj ^:no-warn-when-unused metadata]
(type obj)))

(defmethod make-custom-data-readers :default
[obj mta]
(throw (ex-info "Not a valid data-reader map" (assoc mta :object obj))))
[obj metadata]
(throw (ex-info "Not a valid data-reader map" (assoc metadata :object obj))))

(defmethod make-custom-data-readers basilisp.lang.interfaces/IPersistentMap
[mappings mta]
[mappings metadata]
(reduce (fn [m [k v]]
(let [v' (if (qualified-symbol? v)
(intern (create-ns (symbol (namespace v)))
Expand All @@ -7108,33 +7109,33 @@
(not (qualified-symbol? k))
(throw
(ex-info "Invalid tag in data-readers. Expected qualified symbol."
(merge mta {:form k})))
(merge metadata {:form k})))

(not (ifn? v'))
(throw (ex-info "Invalid reader function in data-readers"
(merge mta {:form v})))
(merge metadata {:form v})))

:else
(assoc m (with-meta k mta) v'))))
(assoc m (with-meta k metadata) v'))))
mappings
mappings))

(defmethod make-custom-data-readers importlib.metadata/EntryPoint
[entry-point mta]
[entry-point metadata]
(make-custom-data-readers (.load entry-point)
(assoc mta
(assoc metadata
:basilisp.entry-point/name (.-name entry-point)
:basilisp.entry-point/group (.-group entry-point))))

(defmethod make-custom-data-readers pathlib/Path
[file mta]
[file metadata]
(make-custom-data-readers
(with-open [rdr (basilisp.io/reader file)]
(read (if (.endswith (name file) "cljc")
{:eof nil :read-cond :allow}
{:eof nil})
rdr))
(assoc mta :file (str file))))
(assoc metadata :file (str file))))

(defn- data-readers-entry-points []
(when (#{"true" "t" "1" "yes" "y"} (.lower
Expand Down
6 changes: 3 additions & 3 deletions src/basilisp/core/protocols.lpy
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler was issuing warnings about these names being unused, so this just silences those warnings.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

(extend-protocol CollReduce
nil
(coll-reduce [s f]
(coll-reduce [_s f]
(f))
(coll-reduce [s f init]
(coll-reduce [_s _f init]
init)

python/object
Expand All @@ -29,7 +29,7 @@

(extend-protocol KVReduce
nil
(kv-reduce [s f init]
(kv-reduce [_s _f init]
init)

python/object
Expand Down
2 changes: 1 addition & 1 deletion src/basilisp/edn.lpy
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@
(.write writer "\"")
nil)
nil
(write* [this writer]
(write* [_this writer]
(.write writer "nil")
nil)

Expand Down
16 changes: 13 additions & 3 deletions src/basilisp/lang/compiler/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
SYM_NO_INLINE_META_KEY,
SYM_NO_WARN_ON_REDEF_META_KEY,
SYM_NO_WARN_ON_SHADOW_META_KEY,
SYM_NO_WARN_ON_VAR_INDIRECTION_META_KEY,
SYM_NO_WARN_WHEN_UNUSED_META_KEY,
SYM_PRIVATE_META_KEY,
SYM_PROPERTY_META_KEY,
Expand Down Expand Up @@ -674,6 +675,7 @@ def get_meta_prop(o: Union[IMeta, Var]) -> Any:
_is_py_staticmethod = _bool_meta_getter(SYM_STATICMETHOD_META_KEY)
_is_macro = _bool_meta_getter(SYM_MACRO_META_KEY)
_is_no_inline = _bool_meta_getter(SYM_NO_INLINE_META_KEY)
_is_allow_var_indirection = _bool_meta_getter(SYM_NO_WARN_ON_VAR_INDIRECTION_META_KEY)
_is_use_var_indirection = _bool_meta_getter(SYM_USE_VAR_INDIRECTION_KEY)
_inline_meta = _meta_getter(SYM_INLINE_META_KW)
_tag_meta = _meta_getter(SYM_TAG_META_KEY)
Expand Down Expand Up @@ -3506,6 +3508,7 @@ def __resolve_namespaced_symbol_in_ns(
return VarRef(
form=form,
var=v,
is_allow_var_indirection=_is_allow_var_indirection(form),
env=ctx.get_node_env(pos=ctx.syntax_position),
)

Expand All @@ -3525,6 +3528,7 @@ def __resolve_namespaced_symbol( # pylint: disable=too-many-branches # noqa: M
return VarRef(
form=form,
var=v,
is_allow_var_indirection=_is_allow_var_indirection(form),
env=ctx.get_node_env(pos=ctx.syntax_position),
)
elif form.ns == _BUILTINS_NS:
Expand All @@ -3549,7 +3553,12 @@ def __resolve_namespaced_symbol( # pylint: disable=too-many-branches # noqa: M
f"cannot resolve private Var {form.name} from namespace {form.ns}",
form=form,
)
return VarRef(form=form, var=v, env=ctx.get_node_env(pos=ctx.syntax_position))
return VarRef(
form=form,
var=v,
is_allow_var_indirection=_is_allow_var_indirection(form),
env=ctx.get_node_env(pos=ctx.syntax_position),
)

if "." in form.name and form.name != _DOUBLE_DOT_MACRO_NAME:
raise ctx.AnalyzerException(
Expand Down Expand Up @@ -3610,6 +3619,7 @@ def __resolve_namespaced_symbol( # pylint: disable=too-many-branches # noqa: M
target=VarRef(
form=form,
var=maybe_type_or_class,
is_allow_var_indirection=_is_allow_var_indirection(form),
env=ctx.get_node_env(pos=ctx.syntax_position),
),
is_assignable=False,
Expand All @@ -3624,8 +3634,7 @@ def __resolve_namespaced_symbol( # pylint: disable=too-many-branches # noqa: M
def __resolve_bare_symbol(
ctx: AnalyzerContext, form: sym.Symbol
) -> Union[Const, MaybeClass, VarRef]:
"""Resolve a non-namespaced symbol into a Python name or a local
Basilisp Var."""
"""Resolve a non-namespaced symbol into a Python name or a local Basilisp Var."""
assert form.ns is None

# Look up the symbol in the namespace mapping of the current namespace.
Expand All @@ -3635,6 +3644,7 @@ def __resolve_bare_symbol(
return VarRef(
form=form,
var=v,
is_allow_var_indirection=_is_allow_var_indirection(form),
env=ctx.get_node_env(pos=ctx.syntax_position),
)

Expand Down
1 change: 1 addition & 0 deletions src/basilisp/lang/compiler/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class SpecialForm:
SYM_MUTABLE_META_KEY = kw.keyword("mutable")
SYM_NO_WARN_ON_REDEF_META_KEY = kw.keyword("no-warn-on-redef")
SYM_NO_WARN_ON_SHADOW_META_KEY = kw.keyword("no-warn-on-shadow")
SYM_NO_WARN_ON_VAR_INDIRECTION_META_KEY = kw.keyword("no-warn-on-var-indirection")
SYM_NO_WARN_WHEN_UNUSED_META_KEY = kw.keyword("no-warn-when-unused")
SYM_REDEF_META_KEY = kw.keyword("redef")
SYM_STATICMETHOD_META_KEY = kw.keyword("staticmethod")
Expand Down
4 changes: 2 additions & 2 deletions src/basilisp/lang/compiler/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ def use_var_indirection(self) -> bool:
@property
def warn_on_var_indirection(self) -> bool:
"""If True, warn when a Var reference cannot be direct linked (iff
use_var_indirection is False).."""
use_var_indirection is False)."""
return not self.use_var_indirection and self._opts.val_at(
WARN_ON_VAR_INDIRECTION, True
)
Expand Down Expand Up @@ -3188,7 +3188,7 @@ def _var_sym_to_py_ast(
if direct_link is not None:
return direct_link

if ctx.warn_on_var_indirection:
if ctx.warn_on_var_indirection and not node.is_allow_var_indirection:
logger.warning(
f"could not resolve a direct link to Var '{var_name}' "
f"({node.env.ns}:{node.env.line})"
Expand Down
1 change: 1 addition & 0 deletions src/basilisp/lang/compiler/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,7 @@ class VarRef(Node[Union[sym.Symbol, ISeq]], Assignable):
env: NodeEnv
return_var: bool = False
is_assignable: bool = True
is_allow_var_indirection: bool = False
children: Sequence[kw.Keyword] = vec.PersistentVector.empty()
op: NodeOp = NodeOp.VAR
top_level: bool = False
Expand Down
13 changes: 13 additions & 0 deletions tests/basilisp/compiler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6501,6 +6501,19 @@ def test_warning_for_cross_ns_alias_reference(
rf"could not resolve a direct link to Var 'm' \({ns}:\d+\)",
)

def test_no_warning_for_cross_ns_alias_reference_if_warning_enabled_but_suppressed_locally(
self, lcompile: CompileFn, other_ns, assert_no_matching_logs
):
lcompile(
"(fn [] (^:no-warn-on-var-indirection other/m :z))",
opts={compiler.WARN_ON_VAR_INDIRECTION: True},
)
assert_no_matching_logs(
"basilisp.lang.compiler.generator",
logging.WARNING,
r"could not resolve a direct link to Var 'm' \(test:\d+\)",
)

def test_no_warning_for_cross_ns_alias_reference_if_warning_disabled(
self, lcompile: CompileFn, other_ns, assert_no_matching_logs
):
Expand Down