From 0cee308fc1fddb280ab627c193a0c987091ed808 Mon Sep 17 00:00:00 2001 From: k-hara Date: Wed, 8 Jul 2015 00:51:13 +0900 Subject: [PATCH] fix Issue 7516 - Postblit not called for structs returned from a ternary operator --- src/dstruct.d | 2 +- src/expression.d | 28 +++++++-- src/expression.h | 2 +- src/func.d | 4 +- test/runnable/sdtor.d | 128 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 156 insertions(+), 8 deletions(-) diff --git a/src/dstruct.d b/src/dstruct.d index aa82e3bb26e3..bb89b2893fee 100644 --- a/src/dstruct.d +++ b/src/dstruct.d @@ -632,7 +632,7 @@ public: L1: if (e.op == TOKerror) return false; - (*elements)[i] = e.isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); + (*elements)[i] = doCopyOrMove(sc, e); } return true; } diff --git a/src/expression.d b/src/expression.d index 164a366e4af9..ab7ec93e9abd 100644 --- a/src/expression.d +++ b/src/expression.d @@ -1056,7 +1056,9 @@ extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* exps, Type t0 = Type.terror; continue; } - e = e.isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); + + e = doCopyOrMove(sc, e); + if (t0 && !t0.equals(e.type)) { /* This applies ?: to merge the types. It's backwards; @@ -1265,6 +1267,24 @@ extern (C++) Expression callCpCtor(Scope* sc, Expression e) return e; } +/************************************************ + * Handle the postblit call on lvalue, or the move of rvalue. + */ +extern (C++) Expression doCopyOrMove(Scope *sc, Expression e) +{ + if (e.op == TOKquestion) + { + CondExp ce = cast(CondExp)e; + ce.e1 = doCopyOrMove(sc, ce.e1); + ce.e2 = doCopyOrMove(sc, ce.e2); + } + else + { + e = e.isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); + } + return e; +} + /**************************************** * Now that we know the exact type of the function we're calling, * the arguments[] need to be adjusted: @@ -1552,7 +1572,7 @@ extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type t } else { - // arg = arg->isLvalue() ? callCpCtor(sc, arg) : valueNoDtor(arg); + //arg = doCopyOrMove(sc, arg); } //printf("arg: %s\n", arg->toChars()); //printf("type: %s\n", arg->type->toChars()); @@ -1759,7 +1779,7 @@ extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type t } else if (ts) { - arg = arg.isLvalue() ? callCpCtor(sc, arg) : valueNoDtor(arg); + arg = doCopyOrMove(sc, arg); } else if (anythrow && firstthrow <= i && i <= lastthrow && gate) { @@ -12532,7 +12552,7 @@ public: if (e2.checkPostblit(sc, tb2)) return new ErrorExp(); e2 = e2.castTo(sc, tb1next); - e2 = e2.isLvalue() ? callCpCtor(sc, e2) : valueNoDtor(e2); + e2 = doCopyOrMove(sc, e2); } else if (tb1.ty == Tarray && (tb1next.ty == Tchar || tb1next.ty == Twchar) && e2.type.ty != tb1next.ty && e2.implicitConvTo(Type.tdchar)) { diff --git a/src/expression.h b/src/expression.h index c34c28a2563c..8b871fd81687 100644 --- a/src/expression.h +++ b/src/expression.h @@ -66,7 +66,7 @@ TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s); Expression *valueNoDtor(Expression *e); int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1); Expression *resolveAliasThis(Scope *sc, Expression *e, bool gag = false); -Expression *callCpCtor(Scope *sc, Expression *e); +Expression *doCopyOrMove(Scope *sc, Expression *e); Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0); Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, IntervalExp *ie, Expression **pe0); Expression *integralPromotions(Expression *e, Scope *sc); diff --git a/src/func.d b/src/func.d index 97c0b3f07cb3..404c6bdfed47 100644 --- a/src/func.d +++ b/src/func.d @@ -1825,8 +1825,8 @@ public: /* Bugzilla 10789: * If NRVO is not possible, all returned lvalues should call their postblits. */ - if (!nrvo_can && exp.isLvalue()) - exp = callCpCtor(sc2, exp); + if (!nrvo_can) + exp = doCopyOrMove(sc2, exp); checkEscape(sc2, exp, false); } exp = checkGC(sc2, exp); diff --git a/test/runnable/sdtor.d b/test/runnable/sdtor.d index 109d0f87bda3..721779204cfa 100644 --- a/test/runnable/sdtor.d +++ b/test/runnable/sdtor.d @@ -2150,6 +2150,129 @@ void test7506() assert(S.di == 3); } +/**********************************/ +// 7516 + +struct S7516 +{ + int val; + + this(int n) { val = n; } + this(this) { val *= 3; } +} + +// CondExp on return statement +void test7516a() +{ + alias S = S7516; + S s1 = S(1); + S s2 = S(2); + + S foo(bool f) { return f ? s1 : s2; } + S hoo(bool f) { return f ? S(1) : S(2); } + S bar(bool f) { return f ? s1 : S(2); } + S baz(bool f) { return f ? S(1) : s2; } + + auto r1 = foo(true); assert(r1.val == 3); + auto r2 = foo(false); assert(r2.val == 6); + auto r3 = hoo(true); assert(r3.val == 1); + auto r4 = hoo(false); assert(r4.val == 2); + auto r5 = bar(true); assert(r5.val == 3); + auto r6 = bar(false); assert(r6.val == 2); + auto r7 = baz(true); assert(r7.val == 1); + auto r8 = baz(false); assert(r8.val == 6); +} + +// CondExp on function argument +void test7516b() +{ + alias S = S7516; + S s1 = S(1); + S s2 = S(2); + S func(S s) { return s; } + + S foo(bool f) { return func(f ? s1 : s2 ); } + S hoo(bool f) { return func(f ? S(1) : S(2)); } + S bar(bool f) { return func(f ? s1 : S(2)); } + S baz(bool f) { return func(f ? S(1) : s2 ); } + + auto r1 = foo(true); assert(r1.val == 3 * 3); + auto r2 = foo(false); assert(r2.val == 6 * 3); + auto r3 = hoo(true); assert(r3.val == 1 * 3); + auto r4 = hoo(false); assert(r4.val == 2 * 3); + auto r5 = bar(true); assert(r5.val == 3 * 3); + auto r6 = bar(false); assert(r6.val == 2 * 3); + auto r7 = baz(true); assert(r7.val == 1 * 3); + auto r8 = baz(false); assert(r8.val == 6 * 3); +} + +// CondExp on array literal +void test7516c() +{ + alias S = S7516; + S s1 = S(1); + S s2 = S(2); + + S[] foo(bool f) { return [f ? s1 : s2 ]; } + S[] hoo(bool f) { return [f ? S(1) : S(2)]; } + S[] bar(bool f) { return [f ? s1 : S(2)]; } + S[] baz(bool f) { return [f ? S(1) : s2 ]; } + + auto r1 = foo(true); assert(r1[0].val == 3); + auto r2 = foo(false); assert(r2[0].val == 6); + auto r3 = hoo(true); assert(r3[0].val == 1); + auto r4 = hoo(false); assert(r4[0].val == 2); + auto r5 = bar(true); assert(r5[0].val == 3); + auto r6 = bar(false); assert(r6[0].val == 2); + auto r7 = baz(true); assert(r7[0].val == 1); + auto r8 = baz(false); assert(r8[0].val == 6); +} + +// CondExp on rhs of cat assign +void test7516d() +{ + alias S = S7516; + S s1 = S(1); + S s2 = S(2); + + S[] foo(bool f) { S[] a; a ~= f ? s1 : s2 ; return a; } + S[] hoo(bool f) { S[] a; a ~= f ? S(1) : S(2); return a; } + S[] bar(bool f) { S[] a; a ~= f ? s1 : S(2); return a; } + S[] baz(bool f) { S[] a; a ~= f ? S(1) : s2 ; return a; } + + auto r1 = foo(true); assert(r1[0].val == 3); + auto r2 = foo(false); assert(r2[0].val == 6); + auto r3 = hoo(true); assert(r3[0].val == 1); + auto r4 = hoo(false); assert(r4[0].val == 2); + auto r5 = bar(true); assert(r5[0].val == 3); + auto r6 = bar(false); assert(r6[0].val == 2); + auto r7 = baz(true); assert(r7[0].val == 1); + auto r8 = baz(false); assert(r8[0].val == 6); +} + +// CondExp on struct literal element +void test7516e() +{ + alias S = S7516; + S s1 = S(1); + S s2 = S(2); + struct X { S s; } + + X foo(bool f) { return X(f ? s1 : s2 ); } + X hoo(bool f) { return X(f ? S(1) : S(2)); } + X bar(bool f) { return X(f ? s1 : S(2)); } + X baz(bool f) { return X(f ? S(1) : s2 ); } + + auto r1 = foo(true); assert(r1.s.val == 3); + auto r2 = foo(false); assert(r2.s.val == 6); + auto r3 = hoo(true); assert(r3.s.val == 1); + auto r4 = hoo(false); assert(r4.s.val == 2); + auto r5 = bar(true); assert(r5.s.val == 3); + auto r6 = bar(false); assert(r6.s.val == 2); + auto r7 = baz(true); assert(r7.s.val == 1); + auto r8 = baz(false); assert(r8.s.val == 6); +} + /**********************************/ // 7530 @@ -4138,6 +4261,11 @@ int main() test7353(); test61(); test7506(); + test7516a(); + test7516b(); + test7516c(); + test7516d(); + test7516e(); test7530(); test62(); test7579a();