-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Change backend name handling #2322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Needs updated backend # Conflicts: # compiler/src/dotty/tools/dotc/core/Names.scala
Also, fix a discrepancy in the signature of newWeakSet # Conflicts: # scala-backend
Avoid using toString directly.
How did I verify that this works? Starting with 2c4ac2c, I added an assertion that It's tricky to make this a CI-suitable test though, because it is inherently sequential. Name#toString does not know about the context, so we need a global variable to control the assertion, which is not thread-safe. So for the moment I submit as is. If toString calls should be introduced in the backend in the future they would likely produce wrong code for symbolic names (once these are in). I am aware that it's a weak barrier against regression. Once we get get parametricity in Any, we can enforce it in the types, so then we will be in the dry. |
How about doing this instead: def toString = {
if (Config.checkBackendNames) {
assert(!Thread.currentThread.getStackTrace.exists(_.toString.contains("dotty.tools.dotc.backend.jvm.GenBCode"),
"Backend should not call Name#toString, Name#mangledString should be used instead."))
}
// ...
} |
Better way to use Thread.currentThread.getStackTrace.exists(_.getClassName == classOf[GenBCode].getName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise LGTM
def javaBinaryName: Name = javaClassName.replace('.', '/').toTypeName // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) | ||
def javaClassName: String = toDenot(sym).fullName.toString// addModuleSuffix(fullNameInternal('.')).toString | ||
def javaSimpleName: String = toDenot(sym).name.mangledString // addModuleSuffix(simpleName.dropLocal) | ||
def javaBinaryName: String = javaClassName.replace('.', '/') // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This TODO is no longer necessary.
@@ -67,6 +67,7 @@ object Names { | |||
def asSimpleName: SimpleTermName | |||
def toSimpleName: SimpleTermName | |||
def mangled: Name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to add some documentation on the meaning/use of mangled
.
But then we'd have to leave in the ~15 exceptions which allow toString when backend calls something else which requires it (such as .member). Possible but ugly. |
These were caught when turning the backend check on.
// We print the stacktrace instead of doing an assert directly, | ||
// because asserts are caught in exception handlers which might | ||
// cause other failures. In that case the first, important failure | ||
// is lost. | ||
println("Backend should not call Name#toString, Name#mangledString should be used instead.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Console.err.println
(or System.err.println
) instead. printStackTrace
will print on stderr, and if you use println right before it which prints on stdout, the output might get mixed.
// We print the stacktrace instead of doing an assert directly, | ||
// because asserts are caught in exception handlers which might | ||
// cause other failures. In that case the first, important failure | ||
// is lost. | ||
println("Backend should not call Name#toString, Name#mangledString should be used instead.") | ||
new Error().printStackTrace() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nicer way: Thread.dumpStack()
@@ -301,6 +305,9 @@ object Names { | |||
new String(chrs, start, length) | |||
} | |||
|
|||
/** It's OK to take a toString if the stacktrace does not occur a method |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: "a method" -> "in a method"
And on the line below: "or it also" -> "or if it also"
!trace.exists(_.getClassName.endsWith("GenBCode")) || | ||
trace.exists(elem => | ||
List( | ||
"mangledString", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternative to defining a whitelist in this way would be to define a new method named something like unmangledString
that does what toString
currently do, and have toString
forward to it. Then we can just change all the code calling toString
to use unmangledString
instead, but keep the stacktrace check in toString
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that would affect too many things. Note that most of the whitelist does not call toString directly, sometimes there are 10-20 calls in between.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've left small suggestion to make checkBackendNames
into a flag, instead of compile-time constant. I don't think this code is performance critical, given that Dotty doesn't call it much at all and backend only calls it once for every name when generates its own representation.
Dotty-wise looks good enough to me.
Checking stack-trace seems to be robust enough, given that is passes in both boostrapped and non-bootstrapped mode.
* so not suitable for continuous testing. But it can be used to find a problem | ||
* when running a specific test. | ||
*/ | ||
final val checkBackendNames = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would propose to make this configurable as a flag. So that we can ask users to enable checked mode in case something looks very strange in their reports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe that's a bit paranoid? So far, errors resulting from producing symbolic strings were very easy to diagnose.
Change backend name handling to support symbolic names. The idea is to
narrow the interface to names in the backend interface and avoid direct calls
to toString on names.