-
-
Notifications
You must be signed in to change notification settings - Fork 608
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
Untangling setUnsafe() #17072
Untangling setUnsafe() #17072
Conversation
Thanks for your pull request, @WalterBright! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "master + dmd#17072" |
79b5e7b
to
b1f84f9
Compare
This installment separates out the special case of function into a separate function, with a separate name instead of an overload. |
extern (D) bool setFunctionToUnsafe(FuncDeclaration fd) | ||
{ | ||
if (fd.safetyInprocess) | ||
{ | ||
fd.safetyInprocess = false; | ||
fd.type.toTypeFunction().trust = TRUST.system; | ||
|
||
if (fd.fes) | ||
setFunctionToUnsafe(fd.fes.func); |
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 understand the renaming, but I don't think it's a good idea to duplicate this function with partially evaluated arguments: it only adds new confusing overloading, and every call to this function is basically a diagnostic bug and should be replaced with a call to the first overload of setFunctionToUnsafe
, which explains to the user why a function is being marked unsafe.
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.
The problems are:
- the multiple different overloadings of setUnsafe
- the tangle of different behaviors taken based on whether fmt is null or arg is null or both are null
- fhe absurdly complicated forwarding of the optional arguments
I've spent far too much time trying to trace through the logic of this function and its partner errorSupplementalInferredAttr()
My plan is to replace this rube goldberg machine with proper use of va_list
. Problem (2) should split the function into 3 distinct functions. This particular PR factors out the null null case.
It's my fault that va_list wasn't used in the original incarnation of this, and I aim to fix it. The first two steps are done - this one, and improving ErrorSink to support va_list.
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 particular PR factors out the null null case.
The null null case is a diagnostic bug. Basically all setUnsafe
calls without error message were being replaced with ones with error messages to fix Issue 17374 (See also: Spelunking Attribute Inference in D), but there were a few tricky ones remaining (especially the infamous inference of recursive functions problem), so I haven't gotten rid of the default argument for the format string yet.
It's my fault that va_list wasn't used in the original incarnation of this
Actually, that would be me 😮
The reason optional arguments are passed as object parameters is because they are being stored in a AttributeViolation
inside a FuncDeclaration
. This is the ddoc comment of AttributeViolation
in dmd/func.d:
Stores a reason why a function failed to infer a function attribute like
@safe
orpure
Has two modes:
- a regular safety error, stored in (fmtStr, arg0, arg1)
- a call to a function without the attribute, which is a special case, because in that case,
that function might recursively also have aAttributeViolation
. This way, in case
of a big call stack, the error can go down all the way to the root cause.
TheFunctionDeclaration
is then stored inarg0
andfmtStr
must benull
.
I agree this is a mess, but it was done for performance reasons: eagerly generating attribute error strings for each attribute for each function could slow down compilation when complex templates / expressions are involved, so AttributeViolation
was an attempt to lazily store an error message.
I considered using delegates instead, but that would likely create closures around every call site of setUnsafe
, which may be undesirable.
There's other places in dmd where error messages are stored / passed around before being printed (grep pMessage
or errorHelper
for examples), so a general solution would be really great, I hope you can help out here! But I don't thing va_list
is going to work here though, because you can't store it in a struct, right?
I suggest sorting out what to do with AttributeViolation
fields in FuncDeclaration
first, because that is the bottleneck that the clumsy setUnsafe
(and also setImpure
etc.) calls are built around.
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.
The solution to the arg ? toChars(arg) : null
inefficiency is to split the function into two:
- determine if there is an error
- if so, then produce the error message
AttributeViolation can use OutBuffer.printf to produce a string, as you are correct in that the va_list
cannot be saved.
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.
The inefficiency has nothing to do with arg ? toChars(arg) : null
. That piece of code is only there because different error messages have a different number of %s
arguments.
The problem is that at the point of calling setUnsafe
, it isn't yet clear whether there is going to be an error. The error might appear later when a @safe
function is trying to call a function that was inferred @system
.
AttributeViolation can use OutBuffer.printf to produce a string
You mean on creation? That would indeed simplify it a lot, but that means many error strings are being generated even when there are no errors. I don't mind, it probably won't be a significant performance hit, I just want to make sure we're on the same page here and aware of the consequences.
The setUnsafe() function is a horrible mess with multiple overloads and undocumented dependencies on various argument compinations. I'm going to try an untangle it.