SI-6879 improves Context.freshName#3401
Conversation
|
Does this apply to the full compiler, or just fresh names in macros? I'd be concerned about the former; I expect this could create (more) instability in mangled names in, for example, protected accessors. (Just using that example as I saw a use of freshname there recently while working on 8642a50.) |
|
The only client of |
|
To be more precise: this will affect macros and quasiquotes at runtime but should not affect compiler. |
|
Looks sensible. LGTM |
Instead of per-compilation unit unique counters, the freshName API now uses a per-Global counter. Fresh names now also contain dollars to exclude clashes with supported user-defined names (the ones without dollar signs). This doesn’t fix the bug, because per-Global counters get created anew every time a new Global is instantiated, and that provides some potential for name clashes even for def macros, but at least it completely excludes clashes in typical situations.
SI-6879 improves Context.freshName
|
@xeno-by / @densh This change conflicts with my current work to make scalac output stable (e.g. same output regardless of order of source files or regardless of separate compilation of subsets of the source files.) Neither scala/bug#6879 nor https://groups.google.com/forum/#!topic/scala-internals/aE81MVdIhCk provides me with a motivating example to show that fresh-counters-per-compilation-unit is too fine-grained for macros. Surely this would only be an issue if snippets of code expanded by macros/quasiquotes in different compilation units were inlined together, untypechecked, then retypechecked. Is that use case actually possible? My proposal is to to make all typer-generate fresh names (including macro and quasiquote) use a counter scoped at the closest enclosing "source method". |
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiqotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations witin the typer phase (in which
out-of-order typechecking is a factor) to use a finder grained fresh
name creator. How fine grained? A fresh name generated as some position `p`
shares the fresh name generator scoped at the closest method or class that
encloses that the outermost enclosing tree at the same position.
This definition is designed to give a shared fresh name creator for all
fresh names generated in `macro1(macro2())`, even if the fresh names are
requiested from with a Typer in the macro enclosed by a synthetic method.
- Chances macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real world cases where the new scheme actaully causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
(cherry picked from commit 69d60cb)
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.
```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286
scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```
Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of scala#3401).
Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.
This commit:
- Changes all known fresh name creations within the typer phase (in which
out-of-order typechecking is a factor) to use a fineer grained fresh
name creator. How fine grained? A fresh name generated as some position
`p` shares the fresh name generator scoped at the closest method or
class that encloses that the outermost enclosing tree at the same
position. This definition is designed to give a shared fresh name
creator for all fresh names generated in `macro1(macro2())`, even if
the fresh names are requiested from with a Typer in the macro enclosed
by a synthetic method.
- Changes macro fresh names to use the same fresh naming scheme as the regular
typechecker. An opt-out compiler option allows the old behaviour, but I'm
interested to find real-world cases where the new scheme actually causes
a problem
In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
(cherry picked from commit 69d60cb)
Instead of per-compilation unit unique counters, the freshName API now
uses a per-Global counter. Fresh names now also contain dollars to exclude
clashes with supported user-defined names (the ones without dollar signs).
This doesn’t fix the bug, because per-Global counters get created anew
every time a new Global is instantiated, and that provides some potential
for name clashes even for def macros, but at least it completely excludes
clashes in typical situations.