Skip to content

Commit 47d1fb1

Browse files
committed
SI-6879 improves Context.freshName
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.
1 parent a242101 commit 47d1fb1

File tree

6 files changed

+61
-21
lines changed

6 files changed

+61
-21
lines changed

src/compiler/scala/reflect/macros/contexts/Names.scala

+22-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ package contexts
44
trait Names {
55
self: Context =>
66

7-
def freshNameCreator = callsiteTyper.context.unit.fresh
7+
import global._
8+
9+
def freshNameCreator = globalFreshNameCreator
810

911
def fresh(): String =
1012
freshName()
@@ -16,11 +18,25 @@ trait Names {
1618
freshName[NameType](name)
1719

1820
def freshName(): String =
19-
freshName("fresh$")
20-
21-
def freshName(name: String): String =
22-
freshNameCreator.newName(name)
21+
freshName(nme.FRESH_PREFIX)
22+
23+
def freshName(name: String): String = {
24+
// In comparison with the first version of freshName, current "fresh" names
25+
// at least can't clash with legible user-written identifiers and are much less likely to clash with each other.
26+
// It is still not good enough however, because the counter gets reset every time we create a new Global.
27+
//
28+
// This would most certainly cause problems if Scala featured something like introduceTopLevel,
29+
// but even for def macros this can lead to unexpected troubles. Imagine that one Global
30+
// creates a term of an anonymous type with a member featuring a "fresh" name, and then another Global
31+
// imports that term with a wildcard and then generates a "fresh" name of its own. Given unlucky
32+
// circumstances these "fresh" names might end up clashing.
33+
//
34+
// TODO: hopefully SI-7823 will provide an ultimate answer to this problem.
35+
// In the meanwhile I will also keep open the original issue: SI-6879 "c.freshName is broken".
36+
val sortOfUniqueSuffix = freshNameCreator.newName(nme.FRESH_SUFFIX)
37+
name + "$" + sortOfUniqueSuffix
38+
}
2339

2440
def freshName[NameType <: Name](name: NameType): NameType =
25-
name.mapName(freshNameCreator.newName(_)).asInstanceOf[NameType]
41+
name.mapName(freshName(_)).asInstanceOf[NameType]
2642
}

src/reflect/scala/reflect/internal/StdNames.scala

+2
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,8 @@ trait StdNames {
305305
val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set"
306306
val SUPER_PREFIX_STRING = "super$"
307307
val WHILE_PREFIX = "while$"
308+
val FRESH_PREFIX = "fresh"
309+
val FRESH_SUFFIX = "macro$" // uses a keyword to avoid collisions with mangled names
308310

309311
// Compiler internal names
310312
val ANYname: NameType = "<anyname>"

src/reflect/scala/reflect/internal/SymbolTable.scala

+4
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ abstract class SymbolTable extends macros.Universe
141141
)
142142
}
143143

144+
// SI-6879 Keeps track of counters that are supposed to be globally unique
145+
// as opposed to traditional freshers that are unique to compilation units.
146+
val globalFreshNameCreator = new FreshNameCreator
147+
144148
/** Dump each symbol to stdout after shutdown.
145149
*/
146150
final val traceSymbolActivity = sys.props contains "scalac.debug.syms"

src/reflect/scala/reflect/macros/Names.scala

+29-11
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,51 @@ package macros
66
* <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span>
77
*
88
* A slice of [[scala.reflect.macros.blackbox.Context the Scala macros context]] that
9-
* provides functions that generate unique names.
9+
* provides functions that generate fresh names.
10+
*
11+
* In the current implementation, fresh names are more or less unique in the sense that
12+
* within the same compilation run they are guaranteed not to clash with:
13+
* 1) Results of past and future invocations of functions of `freshName` family
14+
* 2) User-defined or macro-generated names that don't contain dollar symbols
15+
* 3) Macro-generated names that are created by concatenating names from the first, second and third categories
16+
*
17+
* Uniqueness of fresh names across compilation runs is not guaranteed, but that's something
18+
* that we would like to improve upon in future releases. See [[https://issues.scala-lang.org/browse/SI-6879]] for more information.
19+
*
20+
* @define freshNameNoParams
21+
* Creates a string that represents a more or less unique name.
22+
* Consult [[scala.reflect.macros.Names]] for more information on uniqueness of such names.
23+
*
24+
* @define freshNameStringParam
25+
* Creates a string that represents a more or less unique name having a given prefix.
26+
* Consult [[scala.reflect.macros.Names]] for more information on uniqueness of such names.
27+
*
28+
* @define freshNameNameParam
29+
* Creates a more or less unique name having a given name as a prefix and
30+
* having the same flavor (term name or type name) as the given name.
31+
* Consult [[scala.reflect.macros.Names]] for more information on uniqueness of such names.
1032
*/
1133
trait Names {
1234
self: blackbox.Context =>
1335

14-
/** Creates a unique string. */
36+
/** $freshNameNoParams */
1537
@deprecated("Use freshName instead", "2.11.0")
1638
def fresh(): String
1739

18-
/** Creates a unique string having a given prefix. */
40+
/** $freshNameStringParam */
1941
@deprecated("Use freshName instead", "2.11.0")
2042
def fresh(name: String): String
2143

22-
/** Creates a unique name having a given name as a prefix and
23-
* having the same flavor (term name or type name) as the given name.
24-
*/
44+
/** $freshNameNameParam */
2545
@deprecated("Use freshName instead", "2.11.0")
2646
def fresh[NameType <: Name](name: NameType): NameType
2747

28-
/** Creates a unique string. */
48+
/** $freshNameNoParams */
2949
def freshName(): String
3050

31-
/** Creates a unique string having a given prefix. */
51+
/** $freshNameStringParam */
3252
def freshName(name: String): String
3353

34-
/** Creates a unique name having a given name as a prefix and
35-
* having the same flavor (term name or type name) as the given name.
36-
*/
54+
/** $freshNameNameParam */
3755
def freshName[NameType <: Name](name: NameType): NameType
3856
}

src/reflect/scala/reflect/runtime/JavaUniverse.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class JavaUniverse extends internal.SymbolTable with JavaUniverseForce with Refl
2121
def newStrictTreeCopier: TreeCopier = new StrictTreeCopier
2222
def newLazyTreeCopier: TreeCopier = new LazyTreeCopier
2323

24-
val currentFreshNameCreator = new reflect.internal.util.FreshNameCreator
24+
def currentFreshNameCreator = globalFreshNameCreator
2525

2626
// can't put this in runtime.Trees since that's mixed with Global in ReflectGlobal, which has the definition from internal.Trees
2727
object treeInfo extends {
+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
fresh$1
2-
qwe1
3-
qwe2
1+
fresh$macro$1
2+
qwe$macro$2
3+
qwe$macro$3
44
reflective compilation has failed:
55

66
blargh

0 commit comments

Comments
 (0)