Fix parameter-less out contracts of virtual methods with auto return type

These out contracts can still access the return value via __result.

LDC always passes the return value by reference to the out contract -
as long as there is a result (non-void). As the return type may still
need to be inferred in the first semantic pass, reserve a parameter
there and remove it later in semantic3() in case it is void (or finalize
its type).

Fixes compilable/test17502.
This commit is contained in:
Martin 2017-09-22 23:31:43 +02:00
parent 41aae46b91
commit 1fa90a0381
2 changed files with 36 additions and 9 deletions

View file

@ -1102,8 +1102,13 @@ extern (C++) class FuncDeclaration : Declaration
* even if this functions doesn't have an out contract. * even if this functions doesn't have an out contract.
*/ */
fdensureParams = new Expressions(); fdensureParams = new Expressions();
if (outId) /* The return type may still need to be inferred, so we can't know for
fdensureParams.push(new IdentifierExp(loc, outId)); * sure whether there's a return value to be passed to the out contract.
* Reserve a parameter for now and remove it later in semantic3() in case
* the return type is void (or finalize its type).
*/
if (outId || inferRetType)
fdensureParams.push(new IdentifierExp(loc, outId ? outId : Id.result));
} }
if (fensure) if (fensure)
{ {
@ -1123,15 +1128,19 @@ extern (C++) class FuncDeclaration : Declaration
} }
Loc loc = fensure.loc; Loc loc = fensure.loc;
Parameter p = null; Parameter p = null;
if (outId) version(IN_LLVM)
{ {
p = new Parameter(STCref | STCconst, f.nextOf(), outId, null); if (outId || inferRetType)
version(IN_LLVM)
{ {
p = new Parameter(STCref | STCconst, f.nextOf(), outId ? outId : Id.result, null);
fparams.insert(0, p); fparams.insert(0, p);
} }
else }
else
{
if (outId)
{ {
p = new Parameter(STCref | STCconst, f.nextOf(), outId, null);
fparams.push(p); fparams.push(p);
} }
} }
@ -1915,6 +1924,13 @@ extern (C++) class FuncDeclaration : Declaration
if (!global.params.useIn) if (!global.params.useIn)
freq = null; freq = null;
} }
version(IN_LLVM)
{
// If a void return type has been inferred, remove the return value
// parameter for the out contract reserved in the first semantic pass.
if (inferRetType && fdensureParams && f.next.toBasetype().ty == Tvoid)
fdensureParams.remove(0);
}
if (fens) if (fens)
{ {
/* fensure is composed of the [out] contracts /* fensure is composed of the [out] contracts
@ -1934,7 +1950,17 @@ extern (C++) class FuncDeclaration : Declaration
if (out_params.dim > 0) if (out_params.dim > 0)
{ {
Parameter p = (*out_params)[0]; Parameter p = (*out_params)[0];
p.type = f.next; version(IN_LLVM)
{
if (f.next.toBasetype().ty == Tvoid)
out_params.remove(0);
else
p.type = f.next;
}
else
{
p.type = f.next;
}
} }
} }

View file

@ -1060,8 +1060,9 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
DtoCreateNestedContext(funcGen); DtoCreateNestedContext(funcGen);
if (fd->vresult && !fd->vresult->nestedrefs.dim) // FIXME: not sure here :/ // Declare the special __result variable. If it's captured, it has already
{ // been allocated by DtoCreateNestedContext().
if (fd->vresult) {
DtoVarDeclaration(fd->vresult); DtoVarDeclaration(fd->vresult);
} }