Bug 227: Fix spurious warning of function returning address of local variable. #217
Conversation
…variable. First, this does a small refactor/clean-up of codegen for passing parameters that have been lowered to a COMPOUND_EXPR, spliting the value from the rest of the construction/destruction expression if possible. A similar rewrite is done in build_expr, where the following rewrites are done for simple expressions: ``` cast(T)(e1, e2) => (e1, cast(T)e2) &(e1, e2) => (e1, &e2) *(e1, e2) => (e1, *e2) ``` Moving the COMPOUND_EXPR up to the top allows us to have more control on when inserting dtors into the expression. Where before we may have done (a contrived example): ``` SAVE<tmp = __ctor(), tmp>, __dtor(&tmp), SAVE<tmp = __ctor(), tmp>; ``` Rewriting `&(e1, e2)` into `(e1, &e2)` means we can inspect the right hand side for any side effects, if there are none pressent, then we can bypass saving the comma expression, eliding any artificial temporaries being created by the code generator/optimizer. ``` tmp = __ctor(), __dtor(&tmp), tmp; ``` Finally, for fixing bug 227, the code generation for running destructors on return expressions has been separated from normal expressions, because we should be returning the result directly, rather than returning the temporary created to evaluate the expression before calling its destructors. ``` return &(try { SAVE<*(SAVE<*(SAVE<&(tmp = __ctor(), tmp)>), {}>, __cpctor(this, SAVE<*(SAVE<&(tmp = __ctor(), tmp)>), {}>))>; } finally { __dtor(&tmp); }, SAVE<*(SAVE<*(SAVE<&(tmp = __ctor(), tmp)>), {}>, __cpctor(this, SAVE<*(SAVE<&(tmp = __ctor(), tmp)>), {}>))>); ``` With the first refactor in passing parameters, we remove one layer of temporaries being created in the call to `__cpctor()`. ``` return &(try { SAVE<*(*(SAVE<&(tmp = __ctor(), tmp)>), __cpctor(this, {}))>; } finally { __dtor(&tmp); }, SAVE<*(*(SAVE<&(tmp = __ctor(), tmp)>), __cpctor(this, {}))>); ``` With the second refactor, another layer is removed, as the construction of the temporary is not needed when dereferencing the result of `__cpctor()`. ``` return &(try { *(SAVE<&(tmp = __ctor(), tmp)>);, SAVE<*__cpctor(this, {})>;; } finally { __dtor(&tmp); }, SAVE<*__cpctor(this, {})>;); ``` Finally, moving the return expression into `build_return_dtor`, the return is inserted directory into the try block, removing the bogus temporary that was causing the warning in the first place. ``` try { *(SAVE<&(tmp = __ctor(), tmp)>); return __cpctor(this, {}); } finally { __dtor(&tmp); } ```
Should probably look into writing a pass that un-saves expressions that are not evaluated more than once. But so far this small improvement is a far cry better than what we started with. @jpf91 - Any problems you can think of as a result of this? |
No. Sounds like a good idea 👍 |
OK - let's merge this first as it has no caught regressions. :-) |
@ibuclaw The changes in Consider this code: void assertThrown(T : Throwable = Exception, E)
(lazy E expression,
string msg = null,
string file = __FILE__,
size_t line = __LINE__)
{
import core.exception : AssertError;
try
expression();
catch (T)
return;
throw new AssertError("assertThrown failed: No " ~ T.stringof ~ " was thrown"
~ (msg.length == 0 ? "." : ": ") ~ msg,
file, line);
} (complete example: https://paste.gnome.org/pshloltok compile: Without this pull request:
With this pull request:
The additional (The gcc-4.8 regression is actually caused by the destructor getting uninitialized data. |
// don't want to run the destructor on the incomplete object. | ||
CallExp *ce = (e->op == TOKcall) ? ((CallExp *) e) : NULL; | ||
if (ce != NULL && ce->e1->op == TOKdotvar | ||
&& ((DotVarExp *) ce->e1)->var->isCtorDeclaration()) |
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 guess it is here that triggered the change. Before, only TOKcall
and TOKnew
expressions were wrapped in a try/finally. Now it is all expressions except constructors.
I considered this reasonable, but maybe an exception could be made for temporaries as well. I'll have to think on it.
Improves upon D-Programming-GDC#217 by removing the get_compound_value pass and moving the generation directly into the expression builders. The compiler now only wraps cleanups in a try/finally if the result has side effects. Also omproves d_build_call by only creating temporaries if more than argument needs saving.
Improves upon D-Programming-GDC#217 by removing the get_compound_value pass and moving the generation directly into the expression builders. The compiler now only wraps cleanups in a try/finally if the result has side effects. Also omproves d_build_call by only creating temporaries if more than argument needs saving.
Moving the COMPOUND_EXPR up to the top allows us to have more control on when inserting dtors into the expression.
Where before we may have done (a contrived example):
Rewriting
&(e1, e2)
into(e1, &e2)
means we can inspect the right hand side for any side effects, if there are none pressent, then we can bypass saving the comma expression, eliding any artificial temporaries being created by the code generator/optimizer.With the first refactor in passing parameters, we remove one layer of temporaries being created in the call to
__cpctor()
.With the second refactor, another layer is removed, as the construction of the temporary is not needed when dereferencing the result of
__cpctor()
.Finally, moving the return expression into
build_return_dtor
, the return is inserted directory into the try block, removing the bogus temporary that was causing the warning in the first place.