104 lines
4.5 KiB
Text
104 lines
4.5 KiB
Text
Plan for fixing "mk_fresh_name".
|
|
-----------
|
|
|
|
We want to be able create "unique" names without using global and/or
|
|
thread local variables. The motivations are:
|
|
1- Issue #1601
|
|
2- `tactic.mk_fresh_name : tactic name` breaks referencial transparency.
|
|
We can tolerate this problem since `tactic.mk_fresh_name` is a meta function,
|
|
but the previous item is a big problem.
|
|
3- `mk_fresh_name` is not safe. In Lean 3, we expose `name` objects
|
|
in the Lean API. Thus, malicious users may create names that
|
|
are identical to names that will be returned by `mk_fresh_name` in
|
|
the future (e.g., when type checking a declaration).
|
|
It is conceivable that malicious user may create a type incorrect declaration
|
|
that will type check using this vulnerability.
|
|
|
|
We would first add back the `name_generator` class.
|
|
https://github.com/leanprover/lean/blob/CADE25/src/util/name_generator.h
|
|
|
|
It is really annoying to carry the name generator object around.
|
|
The plumbing can be minimized by using the following two tricks.
|
|
|
|
1- `name_generator::mk_child` returns a new name generator that
|
|
creates names that will never clash with the ones created using
|
|
the parent name generator or any other of its descendants.
|
|
By using `mk_child`, we can avoid passing `name_generator` objects
|
|
and/or writing functions such as
|
|
```
|
|
std::pair<result_type, name_generator> foo(name_generator const & g, expr const & e);
|
|
...
|
|
std::pair<result_type, name_generator> p = foo(g, e);
|
|
result_type r = p.first;
|
|
g = p.second;
|
|
|
|
```
|
|
where an updated name generator is returned. With `mk_child`, we can write
|
|
```
|
|
result_type foo(name_generator const & g, expr const & e);
|
|
...
|
|
result_type r = foo(g.mk_child(), e);
|
|
```
|
|
|
|
2- Internal prefixes. A module `M` may want to create a local/temporary
|
|
name generator that is guaranteed to create names that will
|
|
not clash with names created by users of this module.
|
|
We say it is "local/temporary" because we do not want to
|
|
include a name generator parameter in `M`s interface.
|
|
We can accomplish that by creating a name generator with
|
|
an unique prefix that only module `M` can use.
|
|
The static method
|
|
```
|
|
static name name::mk_internal_unique_name();
|
|
```
|
|
was created for this purpose. However, like `mk_fresh_name`,
|
|
it is not safe, since malicious users may again create the same
|
|
names using the Lean API. So, our plan is to
|
|
a) Delete `name::mk_internal_unique_name`
|
|
b) Use `_M` as a prefix for module `M` if `M` is
|
|
not a kernel module.
|
|
c) For the kernel type checker we use the prefix
|
|
`_kernel`, and throw an error if the input term
|
|
contains a name with the prefix `_kernel`.
|
|
Remark: this approach is safer than passing a name generator
|
|
as an argument to the kernel. A malicious user may manually
|
|
create the names that will be created by the name generator
|
|
in the same way he would do it in the scenario above where
|
|
`mk_fresh_name` is used.
|
|
|
|
|
|
The current implementation also uses `mk_tagged_fresh_name`. This
|
|
procedure is used by modules that need to create fresh names, and an
|
|
efficient method for detecting fresh names created by them. The idea
|
|
is simple, it just prefix the fresh name with an unique prefix. We can
|
|
simulate this by adding the method
|
|
`name_generator::mk_tagged_child(name const & prefix)`. Here `prefix`
|
|
must be an atomic internal name (e.g., `_inst`, `to_fmt`, etc). It
|
|
will be implemented as
|
|
```
|
|
name_generator mk_tagged_child(name const & prefix) {
|
|
lean_assert(prefix.is_atomic());
|
|
lean_assert(is_internal_name(prefix));
|
|
return name_generator(prefix + next());
|
|
}
|
|
```
|
|
|
|
In the new design, the parser has a name_generator and
|
|
saves it in the parser snapshots. The methods `name_generator::mk_child`
|
|
and `name_generator::mk_tagged_child` to propagate the `name_generator`.
|
|
No synchronization is needed between threads. The kernel type checker
|
|
does not take `name_generator`'s as described above.
|
|
The class `abstract_type_context` should provide a `mk_fresh_name` primitive.
|
|
|
|
Impact on the cache
|
|
-------------------
|
|
|
|
We have caches in thread local storage, and some of them
|
|
rely on the fact that the names returned by `mk_fresh_name` are always
|
|
different from previously generated names. This assumption will
|
|
not be true after we replace `mk_fresh_name` with the `name_generator` object.
|
|
So, we plan to implement a thread local storage reset operation described at commit
|
|
ec1a490a1. The reset operation should be performed whenever we restore a parser snapshot.
|
|
|
|
|
|
To be continued....
|