From 320581bab36aefbf0fbe4719d4a476af535067c5 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:10:32 +0300 Subject: [PATCH 01/13] Move resolvePropertiesX to expressionsem and make it private --- src/ddmd/dstruct.d | 1 + src/ddmd/expression.d | 263 -------------------------------------- src/ddmd/expressionsem.d | 265 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 263 deletions(-) diff --git a/src/ddmd/dstruct.d b/src/ddmd/dstruct.d index 25eda16206be..b9864be8491d 100644 --- a/src/ddmd/dstruct.d +++ b/src/ddmd/dstruct.d @@ -23,6 +23,7 @@ import ddmd.dsymbolsem; import ddmd.dtemplate; import ddmd.errors; import ddmd.expression; +import ddmd.expressionsem; import ddmd.func; import ddmd.globals; import ddmd.id; diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 5865998cc60d..fef419a3ee21 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -452,269 +452,6 @@ extern (C++) bool isNeedThisScope(Scope* sc, Declaration d) return true; } -/*************************************** - * Pull out any properties. - */ -extern (C++) Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) -{ - //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null); - Loc loc = e1.loc; - - OverloadSet os; - Dsymbol s; - Objects* tiargs; - Type tthis; - if (e1.op == TOKdot) - { - DotExp de = cast(DotExp)e1; - if (de.e2.op == TOKoverloadset) - { - tiargs = null; - tthis = de.e1.type; - os = (cast(OverExp)de.e2).vars; - goto Los; - } - } - else if (e1.op == TOKoverloadset) - { - tiargs = null; - tthis = null; - os = (cast(OverExp)e1).vars; - Los: - assert(os); - FuncDeclaration fd = null; - if (e2) - { - e2 = e2.expressionSemantic(sc); - if (e2.op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - - Expressions a; - a.push(e2); - - for (size_t i = 0; i < os.a.dim; i++) - { - FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, &a, 1); - if (f) - { - if (f.errors) - return new ErrorExp(); - fd = f; - assert(fd.type.ty == Tfunction); - TypeFunction tf = cast(TypeFunction)fd.type; - } - } - if (fd) - { - Expression e = new CallExp(loc, e1, e2); - return e.expressionSemantic(sc); - } - } - { - for (size_t i = 0; i < os.a.dim; i++) - { - FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, null, 1); - if (f) - { - if (f.errors) - return new ErrorExp(); - fd = f; - assert(fd.type.ty == Tfunction); - TypeFunction tf = cast(TypeFunction)fd.type; - if (!tf.isref && e2) - goto Leproplvalue; - } - } - if (fd) - { - Expression e = new CallExp(loc, e1); - if (e2) - e = new AssignExp(loc, e, e2); - return e.expressionSemantic(sc); - } - } - if (e2) - goto Leprop; - } - else if (e1.op == TOKdotti) - { - DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1; - if (!dti.findTempDecl(sc)) - goto Leprop; - if (!dti.ti.semanticTiargs(sc)) - goto Leprop; - tiargs = dti.ti.tiargs; - tthis = dti.e1.type; - if ((os = dti.ti.tempdecl.isOverloadSet()) !is null) - goto Los; - if ((s = dti.ti.tempdecl) !is null) - goto Lfd; - } - else if (e1.op == TOKdottd) - { - DotTemplateExp dte = cast(DotTemplateExp)e1; - s = dte.td; - tiargs = null; - tthis = dte.e1.type; - goto Lfd; - } - else if (e1.op == TOKscope) - { - s = (cast(ScopeExp)e1).sds; - TemplateInstance ti = s.isTemplateInstance(); - if (ti && !ti.semanticRun && ti.tempdecl) - { - //assert(ti.needsTypeInference(sc)); - if (!ti.semanticTiargs(sc)) - goto Leprop; - tiargs = ti.tiargs; - tthis = null; - if ((os = ti.tempdecl.isOverloadSet()) !is null) - goto Los; - if ((s = ti.tempdecl) !is null) - goto Lfd; - } - } - else if (e1.op == TOKtemplate) - { - s = (cast(TemplateExp)e1).td; - tiargs = null; - tthis = null; - goto Lfd; - } - else if (e1.op == TOKdotvar && e1.type && e1.type.toBasetype().ty == Tfunction) - { - DotVarExp dve = cast(DotVarExp)e1; - s = dve.var.isFuncDeclaration(); - tiargs = null; - tthis = dve.e1.type; - goto Lfd; - } - else if (e1.op == TOKvar && e1.type && e1.type.toBasetype().ty == Tfunction) - { - s = (cast(VarExp)e1).var.isFuncDeclaration(); - tiargs = null; - tthis = null; - Lfd: - assert(s); - if (e2) - { - e2 = e2.expressionSemantic(sc); - if (e2.op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - - Expressions a; - a.push(e2); - - FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1); - if (fd && fd.type) - { - if (fd.errors) - return new ErrorExp(); - assert(fd.type.ty == Tfunction); - TypeFunction tf = cast(TypeFunction)fd.type; - Expression e = new CallExp(loc, e1, e2); - return e.expressionSemantic(sc); - } - } - { - FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, null, 1); - if (fd && fd.type) - { - if (fd.errors) - return new ErrorExp(); - assert(fd.type.ty == Tfunction); - TypeFunction tf = cast(TypeFunction)fd.type; - if (!e2 || tf.isref) - { - Expression e = new CallExp(loc, e1); - if (e2) - e = new AssignExp(loc, e, e2); - return e.expressionSemantic(sc); - } - } - } - if (FuncDeclaration fd = s.isFuncDeclaration()) - { - // Keep better diagnostic message for invalid property usage of functions - assert(fd.type.ty == Tfunction); - TypeFunction tf = cast(TypeFunction)fd.type; - Expression e = new CallExp(loc, e1, e2); - return e.expressionSemantic(sc); - } - if (e2) - goto Leprop; - } - if (e1.op == TOKvar) - { - VarExp ve = cast(VarExp)e1; - VarDeclaration v = ve.var.isVarDeclaration(); - if (v && ve.checkPurity(sc, v)) - return new ErrorExp(); - } - if (e2) - return null; - - if (e1.type && e1.op != TOKtype) // function type is not a property - { - /* Look for e1 being a lazy parameter; rewrite as delegate call - */ - if (e1.op == TOKvar) - { - VarExp ve = cast(VarExp)e1; - if (ve.var.storage_class & STClazy) - { - Expression e = new CallExp(loc, e1); - return e.expressionSemantic(sc); - } - } - else if (e1.op == TOKdotvar) - { - // Check for reading overlapped pointer field in @safe code. - if (checkUnsafeAccess(sc, e1, true, true)) - return new ErrorExp(); - } - else if (e1.op == TOKdot) - { - e1.error("expression has no value"); - return new ErrorExp(); - } - else if (e1.op == TOKcall) - { - CallExp ce = cast(CallExp)e1; - // Check for reading overlapped pointer field in @safe code. - if (checkUnsafeAccess(sc, ce.e1, true, true)) - return new ErrorExp(); - } - } - - if (!e1.type) - { - error(loc, "cannot resolve type for %s", e1.toChars()); - e1 = new ErrorExp(); - } - return e1; - -Leprop: - error(loc, "not a property %s", e1.toChars()); - return new ErrorExp(); - -Leproplvalue: - error(loc, "%s is not an lvalue", e1.toChars()); - return new ErrorExp(); -} - -extern (C++) Expression resolveProperties(Scope* sc, Expression e) -{ - //printf("resolveProperties(%s)\n", e.toChars()); - e = resolvePropertiesX(sc, e); - if (e.checkRightThis(sc)) - return new ErrorExp(); - return e; -} - /****************************** * Check the tail CallExp is really property function call. * Bugs: diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index 4faf0c19bd5d..e09b1629501a 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -68,6 +68,271 @@ import ddmd.visitor; enum LOGSEMANTIC = false; +/*************************************** + * Pull out any properties. + */ +private extern (C++) Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) +{ + //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null); + Loc loc = e1.loc; + + OverloadSet os; + Dsymbol s; + Objects* tiargs; + Type tthis; + if (e1.op == TOKdot) + { + DotExp de = cast(DotExp)e1; + if (de.e2.op == TOKoverloadset) + { + tiargs = null; + tthis = de.e1.type; + os = (cast(OverExp)de.e2).vars; + goto Los; + } + } + else if (e1.op == TOKoverloadset) + { + tiargs = null; + tthis = null; + os = (cast(OverExp)e1).vars; + Los: + assert(os); + FuncDeclaration fd = null; + if (e2) + { + e2 = e2.expressionSemantic(sc); + if (e2.op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); + + Expressions a; + a.push(e2); + + for (size_t i = 0; i < os.a.dim; i++) + { + FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, &a, 1); + if (f) + { + if (f.errors) + return new ErrorExp(); + fd = f; + assert(fd.type.ty == Tfunction); + TypeFunction tf = cast(TypeFunction)fd.type; + } + } + if (fd) + { + Expression e = new CallExp(loc, e1, e2); + return e.expressionSemantic(sc); + } + } + { + for (size_t i = 0; i < os.a.dim; i++) + { + FuncDeclaration f = resolveFuncCall(loc, sc, os.a[i], tiargs, tthis, null, 1); + if (f) + { + if (f.errors) + return new ErrorExp(); + fd = f; + assert(fd.type.ty == Tfunction); + TypeFunction tf = cast(TypeFunction)fd.type; + if (!tf.isref && e2) + goto Leproplvalue; + } + } + if (fd) + { + Expression e = new CallExp(loc, e1); + if (e2) + e = new AssignExp(loc, e, e2); + return e.expressionSemantic(sc); + } + } + if (e2) + goto Leprop; + } + else if (e1.op == TOKdotti) + { + DotTemplateInstanceExp dti = cast(DotTemplateInstanceExp)e1; + if (!dti.findTempDecl(sc)) + goto Leprop; + if (!dti.ti.semanticTiargs(sc)) + goto Leprop; + tiargs = dti.ti.tiargs; + tthis = dti.e1.type; + if ((os = dti.ti.tempdecl.isOverloadSet()) !is null) + goto Los; + if ((s = dti.ti.tempdecl) !is null) + goto Lfd; + } + else if (e1.op == TOKdottd) + { + DotTemplateExp dte = cast(DotTemplateExp)e1; + s = dte.td; + tiargs = null; + tthis = dte.e1.type; + goto Lfd; + } + else if (e1.op == TOKscope) + { + s = (cast(ScopeExp)e1).sds; + TemplateInstance ti = s.isTemplateInstance(); + if (ti && !ti.semanticRun && ti.tempdecl) + { + //assert(ti.needsTypeInference(sc)); + if (!ti.semanticTiargs(sc)) + goto Leprop; + tiargs = ti.tiargs; + tthis = null; + if ((os = ti.tempdecl.isOverloadSet()) !is null) + goto Los; + if ((s = ti.tempdecl) !is null) + goto Lfd; + } + } + else if (e1.op == TOKtemplate) + { + s = (cast(TemplateExp)e1).td; + tiargs = null; + tthis = null; + goto Lfd; + } + else if (e1.op == TOKdotvar && e1.type && e1.type.toBasetype().ty == Tfunction) + { + DotVarExp dve = cast(DotVarExp)e1; + s = dve.var.isFuncDeclaration(); + tiargs = null; + tthis = dve.e1.type; + goto Lfd; + } + else if (e1.op == TOKvar && e1.type && e1.type.toBasetype().ty == Tfunction) + { + s = (cast(VarExp)e1).var.isFuncDeclaration(); + tiargs = null; + tthis = null; + Lfd: + assert(s); + if (e2) + { + e2 = e2.expressionSemantic(sc); + if (e2.op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); + + Expressions a; + a.push(e2); + + FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1); + if (fd && fd.type) + { + if (fd.errors) + return new ErrorExp(); + assert(fd.type.ty == Tfunction); + TypeFunction tf = cast(TypeFunction)fd.type; + Expression e = new CallExp(loc, e1, e2); + return e.expressionSemantic(sc); + } + } + { + FuncDeclaration fd = resolveFuncCall(loc, sc, s, tiargs, tthis, null, 1); + if (fd && fd.type) + { + if (fd.errors) + return new ErrorExp(); + assert(fd.type.ty == Tfunction); + TypeFunction tf = cast(TypeFunction)fd.type; + if (!e2 || tf.isref) + { + Expression e = new CallExp(loc, e1); + if (e2) + e = new AssignExp(loc, e, e2); + return e.expressionSemantic(sc); + } + } + } + if (FuncDeclaration fd = s.isFuncDeclaration()) + { + // Keep better diagnostic message for invalid property usage of functions + assert(fd.type.ty == Tfunction); + TypeFunction tf = cast(TypeFunction)fd.type; + Expression e = new CallExp(loc, e1, e2); + return e.expressionSemantic(sc); + } + if (e2) + goto Leprop; + } + if (e1.op == TOKvar) + { + VarExp ve = cast(VarExp)e1; + VarDeclaration v = ve.var.isVarDeclaration(); + if (v && ve.checkPurity(sc, v)) + return new ErrorExp(); + } + if (e2) + return null; + + if (e1.type && e1.op != TOKtype) // function type is not a property + { + /* Look for e1 being a lazy parameter; rewrite as delegate call + */ + if (e1.op == TOKvar) + { + VarExp ve = cast(VarExp)e1; + if (ve.var.storage_class & STClazy) + { + Expression e = new CallExp(loc, e1); + return e.expressionSemantic(sc); + } + } + else if (e1.op == TOKdotvar) + { + // Check for reading overlapped pointer field in @safe code. + if (checkUnsafeAccess(sc, e1, true, true)) + return new ErrorExp(); + } + else if (e1.op == TOKdot) + { + e1.error("expression has no value"); + return new ErrorExp(); + } + else if (e1.op == TOKcall) + { + CallExp ce = cast(CallExp)e1; + // Check for reading overlapped pointer field in @safe code. + if (checkUnsafeAccess(sc, ce.e1, true, true)) + return new ErrorExp(); + } + } + + if (!e1.type) + { + error(loc, "cannot resolve type for %s", e1.toChars()); + e1 = new ErrorExp(); + } + return e1; + +Leprop: + error(loc, "not a property %s", e1.toChars()); + return new ErrorExp(); + +Leproplvalue: + error(loc, "%s is not an lvalue", e1.toChars()); + return new ErrorExp(); +} + +extern (C++) Expression resolveProperties(Scope* sc, Expression e) +{ + //printf("resolveProperties(%s)\n", e.toChars()); + e = resolvePropertiesX(sc, e); + if (e.checkRightThis(sc)) + return new ErrorExp(); + return e; +} + + + private extern (C++) final class ExpressionSemanticVisitor : Visitor { alias visit = super.visit; From d6cba8823d5b6f8099f0393a261a131efaffb5de Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:14:34 +0300 Subject: [PATCH 02/13] Make expression.checkPropertyCall private --- src/ddmd/expression.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index fef419a3ee21..eeda55cc08f2 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -457,7 +457,7 @@ extern (C++) bool isNeedThisScope(Scope* sc, Declaration d) * Bugs: * This doesn't appear to do anything. */ -extern (C++) bool checkPropertyCall(Expression e, Expression emsg) +private extern (C++) bool checkPropertyCall(Expression e, Expression emsg) { while (e.op == TOKcomma) e = (cast(CommaExp)e).e2; From b9fd233faee9f0804122455b4f8366e84db68c5c Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:15:21 +0300 Subject: [PATCH 03/13] Make expression.searchUFCS private --- src/ddmd/expression.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index eeda55cc08f2..b2dc09a4ba1a 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -586,7 +586,7 @@ extern (C++) Expression resolvePropertiesOnly(Scope* sc, Expression e1) /****************************** * Find symbol in accordance with the UFCS name look up rule */ -extern (C++) Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) +private extern (C++) Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) { //printf("searchUFCS(ident = %s)\n", ident.toChars()); Loc loc = ue.loc; From 593623ee80007221a4130cfe3dc6436d7b0ff4eb Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:33:11 +0300 Subject: [PATCH 04/13] Move arrayExpressionToCommonType to expressionsem and make it private --- src/ddmd/expression.d | 113 --------------------------------------- src/ddmd/expressionsem.d | 111 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 113 deletions(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index b2dc09a4ba1a..ff997ec6c8e4 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1049,119 +1049,6 @@ extern (C++) int expandAliasThisTuples(Expressions* exps, size_t starti = 0) return -1; } -/**************************************** - * The common type is determined by applying ?: to each pair. - * Output: - * exps[] properties resolved, implicitly cast to common type, rewritten in place - * *pt if pt is not NULL, set to the common type - * Returns: - * true a semantic error was detected - */ -extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* exps, Type* pt) -{ - /* Still have a problem with: - * ubyte[][] = [ cast(ubyte[])"hello", [1]]; - * which works if the array literal is initialized top down with the ubyte[][] - * type, but fails with this function doing bottom up typing. - */ - - //printf("arrayExpressionToCommonType()\n"); - scope IntegerExp integerexp = new IntegerExp(0); - scope CondExp condexp = new CondExp(Loc(), integerexp, null, null); - - Type t0 = null; - Expression e0 = null; - size_t j0 = ~0; - - for (size_t i = 0; i < exps.dim; i++) - { - Expression e = (*exps)[i]; - if (!e) - continue; - - e = resolveProperties(sc, e); - if (!e.type) - { - e.error("%s has no value", e.toChars()); - t0 = Type.terror; - continue; - } - if (e.op == TOKtype) - { - e.checkValue(); // report an error "type T has no value" - t0 = Type.terror; - continue; - } - if (e.type.ty == Tvoid) - { - // void expressions do not concur to the determination of the common - // type. - continue; - } - if (checkNonAssignmentArrayOp(e)) - { - t0 = Type.terror; - continue; - } - - e = doCopyOrMove(sc, e); - - if (t0 && !t0.equals(e.type)) - { - /* This applies ?: to merge the types. It's backwards; - * ?: should call this function to merge types. - */ - condexp.type = null; - condexp.e1 = e0; - condexp.e2 = e; - condexp.loc = e.loc; - Expression ex = condexp.expressionSemantic(sc); - if (ex.op == TOKerror) - e = ex; - else - { - (*exps)[j0] = condexp.e1; - e = condexp.e2; - } - } - j0 = i; - e0 = e; - t0 = e.type; - if (e.op != TOKerror) - (*exps)[i] = e; - } - - if (!t0) - t0 = Type.tvoid; // [] is typed as void[] - else if (t0.ty != Terror) - { - for (size_t i = 0; i < exps.dim; i++) - { - Expression e = (*exps)[i]; - if (!e) - continue; - - e = e.implicitCastTo(sc, t0); - //assert(e.op != TOKerror); - if (e.op == TOKerror) - { - /* https://issues.dlang.org/show_bug.cgi?id=13024 - * a workaround for the bug in typeMerge - - * it should paint e1 and e2 by deduced common type, - * but doesn't in this particular case. - */ - t0 = Type.terror; - break; - } - (*exps)[i] = e; - } - } - if (pt) - *pt = t0; - - return (t0 == Type.terror); -} - /**************************************** * Get TemplateDeclaration enclosing FuncDeclaration. */ diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index e09b1629501a..98e691caeb93 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -331,7 +331,118 @@ extern (C++) Expression resolveProperties(Scope* sc, Expression e) return e; } +/**************************************** + * The common type is determined by applying ?: to each pair. + * Output: + * exps[] properties resolved, implicitly cast to common type, rewritten in place + * *pt if pt is not NULL, set to the common type + * Returns: + * true a semantic error was detected + */ +private extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* exps, Type* pt) +{ + /* Still have a problem with: + * ubyte[][] = [ cast(ubyte[])"hello", [1]]; + * which works if the array literal is initialized top down with the ubyte[][] + * type, but fails with this function doing bottom up typing. + */ + + //printf("arrayExpressionToCommonType()\n"); + scope IntegerExp integerexp = new IntegerExp(0); + scope CondExp condexp = new CondExp(Loc(), integerexp, null, null); + + Type t0 = null; + Expression e0 = null; + size_t j0 = ~0; + + for (size_t i = 0; i < exps.dim; i++) + { + Expression e = (*exps)[i]; + if (!e) + continue; + + e = resolveProperties(sc, e); + if (!e.type) + { + e.error("%s has no value", e.toChars()); + t0 = Type.terror; + continue; + } + if (e.op == TOKtype) + { + e.checkValue(); // report an error "type T has no value" + t0 = Type.terror; + continue; + } + if (e.type.ty == Tvoid) + { + // void expressions do not concur to the determination of the common + // type. + continue; + } + if (checkNonAssignmentArrayOp(e)) + { + t0 = Type.terror; + continue; + } + + e = doCopyOrMove(sc, e); + if (t0 && !t0.equals(e.type)) + { + /* This applies ?: to merge the types. It's backwards; + * ?: should call this function to merge types. + */ + condexp.type = null; + condexp.e1 = e0; + condexp.e2 = e; + condexp.loc = e.loc; + Expression ex = condexp.expressionSemantic(sc); + if (ex.op == TOKerror) + e = ex; + else + { + (*exps)[j0] = condexp.e1; + e = condexp.e2; + } + } + j0 = i; + e0 = e; + t0 = e.type; + if (e.op != TOKerror) + (*exps)[i] = e; + } + + if (!t0) + t0 = Type.tvoid; // [] is typed as void[] + else if (t0.ty != Terror) + { + for (size_t i = 0; i < exps.dim; i++) + { + Expression e = (*exps)[i]; + if (!e) + continue; + + e = e.implicitCastTo(sc, t0); + //assert(e.op != TOKerror); + if (e.op == TOKerror) + { + /* https://issues.dlang.org/show_bug.cgi?id=13024 + * a workaround for the bug in typeMerge - + * it should paint e1 and e2 by deduced common type, + * but doesn't in this particular case. + */ + t0 = Type.terror; + break; + } + (*exps)[i] = e; + } + } + if (pt) + *pt = t0; + + return (t0 == Type.terror); +} private extern (C++) final class ExpressionSemanticVisitor : Visitor { From aaf6c5d130c92106a1a9d7c4ffe5d4f77de87991 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:35:42 +0300 Subject: [PATCH 05/13] Move preFunctionParameters to expressionsem and make it private --- src/ddmd/expression.d | 35 ----------------------------------- src/ddmd/expressionsem.d | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index ff997ec6c8e4..542bd03cdcf2 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1066,41 +1066,6 @@ extern (C++) TemplateDeclaration getFuncTemplateDecl(Dsymbol s) return null; } -/**************************************** - * Preprocess arguments to function. - * Output: - * exps[] tuples expanded, properties resolved, rewritten in place - * Returns: - * true a semantic error occurred - */ -extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* exps) -{ - bool err = false; - if (exps) - { - expandTuples(exps); - - for (size_t i = 0; i < exps.dim; i++) - { - Expression arg = (*exps)[i]; - arg = resolveProperties(sc, arg); - if (arg.op == TOKtype) - { - arg.error("cannot pass type %s as a function argument", arg.toChars()); - arg = new ErrorExp(); - err = true; - } - else if (checkNonAssignmentArrayOp(arg)) - { - arg = new ErrorExp(); - err = true; - } - (*exps)[i] = arg; - } - } - return err; -} - /************************************************ * If we want the value of this expression, but do not want to call * the destructor on it. diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index 98e691caeb93..b5f0052dfeff 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -444,6 +444,41 @@ private extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* ex return (t0 == Type.terror); } +/**************************************** + * Preprocess arguments to function. + * Output: + * exps[] tuples expanded, properties resolved, rewritten in place + * Returns: + * true a semantic error occurred + */ +private extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* exps) +{ + bool err = false; + if (exps) + { + expandTuples(exps); + + for (size_t i = 0; i < exps.dim; i++) + { + Expression arg = (*exps)[i]; + arg = resolveProperties(sc, arg); + if (arg.op == TOKtype) + { + arg.error("cannot pass type %s as a function argument", arg.toChars()); + arg = new ErrorExp(); + err = true; + } + else if (checkNonAssignmentArrayOp(arg)) + { + arg = new ErrorExp(); + err = true; + } + (*exps)[i] = arg; + } + } + return err; +} + private extern (C++) final class ExpressionSemanticVisitor : Visitor { alias visit = super.visit; From e011b4715bba606a81bcbfbdf60434f157842885 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:38:07 +0300 Subject: [PATCH 06/13] Make callCpCtor private --- src/ddmd/expression.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 542bd03cdcf2..7b6b72f0234f 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1148,7 +1148,7 @@ extern (C++) bool checkDefCtor(Loc loc, Type t) * Input: * sc just used to specify the scope of created temporary variable */ -extern (C++) Expression callCpCtor(Scope* sc, Expression e) +private extern (C++) Expression callCpCtor(Scope* sc, Expression e) { Type tv = e.type.baseElemOf(); if (tv.ty == Tstruct) From f2afe8abb5d8d83e3b262320b320e99eb42121e1 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:42:50 +0300 Subject: [PATCH 07/13] Move functionParameters to expressionsem and make it private --- src/ddmd/expression.d | 641 -------------------------------------- src/ddmd/expressionsem.d | 643 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 643 insertions(+), 641 deletions(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 7b6b72f0234f..570557d751e9 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1193,647 +1193,6 @@ extern (C++) Expression doCopyOrMove(Scope *sc, Expression e) return e; } -/**************************************** - * Now that we know the exact type of the function we're calling, - * the arguments[] need to be adjusted: - * 1. implicitly convert argument to the corresponding parameter type - * 2. add default arguments for any missing arguments - * 3. do default promotions on arguments corresponding to ... - * 4. add hidden _arguments[] argument - * 5. call copy constructor for struct value arguments - * Input: - * tf type of the function - * fd the function being called, NULL if called indirectly - * Output: - * *prettype return type of function - * *peprefix expression to execute before arguments[] are evaluated, NULL if none - * Returns: - * true errors happened - */ -extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix) -{ - //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); - assert(arguments); - assert(fd || tf.next); - size_t nargs = arguments ? arguments.dim : 0; - size_t nparams = Parameter.dim(tf.parameters); - uint olderrors = global.errors; - bool err = false; - *prettype = Type.terror; - Expression eprefix = null; - *peprefix = null; - - if (nargs > nparams && tf.varargs == 0) - { - error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", cast(ulong)nparams, cast(ulong)nargs, tf.toChars()); - return true; - } - - // If inferring return type, and semantic3() needs to be run if not already run - if (!tf.next && fd.inferRetType) - { - fd.functionSemantic(); - } - else if (fd && fd.parent) - { - TemplateInstance ti = fd.parent.isTemplateInstance(); - if (ti && ti.tempdecl) - { - fd.functionSemantic3(); - } - } - bool isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration(); - - size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) - - /* If the function return type has wildcards in it, we'll need to figure out the actual type - * based on the actual argument types. - */ - MOD wildmatch = 0; - if (tthis && tf.isWild() && !isCtorCall) - { - Type t = tthis; - if (t.isImmutable()) - wildmatch = MODimmutable; - else if (t.isWildConst()) - wildmatch = MODwildconst; - else if (t.isWild()) - wildmatch = MODwild; - else if (t.isConst()) - wildmatch = MODconst; - else - wildmatch = MODmutable; - } - - int done = 0; - for (size_t i = 0; i < n; i++) - { - Expression arg; - - if (i < nargs) - arg = (*arguments)[i]; - else - arg = null; - - if (i < nparams) - { - Parameter p = Parameter.getNth(tf.parameters, i); - - if (!arg) - { - if (!p.defaultArg) - { - if (tf.varargs == 2 && i + 1 == nparams) - goto L2; - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; - } - arg = p.defaultArg; - arg = inlineCopy(arg, sc); - // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ - arg = arg.resolveLoc(loc, sc); - arguments.push(arg); - nargs++; - } - - if (tf.varargs == 2 && i + 1 == nparams) - { - //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); - { - MATCH m; - if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) - { - if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) - goto L2; - else if (nargs != nparams) - { - error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); - return true; - } - goto L1; - } - } - L2: - Type tb = p.type.toBasetype(); - Type tret = p.isLazyArray(); - switch (tb.ty) - { - case Tsarray: - case Tarray: - { - /* Create a static array variable v of type arg.type: - * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; - * - * The array literal in the initializer of the hidden variable - * is now optimized. - * https://issues.dlang.org/show_bug.cgi?id=2356 - */ - Type tbn = (cast(TypeArray)tb).next; - Type tsa = tbn.sarrayOf(nargs - i); - - auto elements = new Expressions(); - elements.setDim(nargs - i); - for (size_t u = 0; u < elements.dim; u++) - { - Expression a = (*arguments)[i + u]; - if (tret && a.implicitConvTo(tret)) - { - a = a.implicitCastTo(sc, tret); - a = a.optimize(WANTvalue); - a = toDelegate(a, a.type, sc); - } - else - a = a.implicitCastTo(sc, tbn); - (*elements)[u] = a; - } - // https://issues.dlang.org/show_bug.cgi?id=14395 - // Convert to a static array literal, or its slice. - arg = new ArrayLiteralExp(loc, elements); - arg.type = tsa; - if (tb.ty == Tarray) - { - arg = new SliceExp(loc, arg, null, null); - arg.type = p.type; - } - break; - } - case Tclass: - { - /* Set arg to be: - * new Tclass(arg0, arg1, ..., argn) - */ - auto args = new Expressions(); - args.setDim(nargs - i); - for (size_t u = i; u < nargs; u++) - (*args)[u - i] = (*arguments)[u]; - arg = new NewExp(loc, null, null, p.type, args); - break; - } - default: - if (!arg) - { - error(loc, "not enough arguments"); - return true; - } - break; - } - arg = arg.expressionSemantic(sc); - //printf("\targ = '%s'\n", arg.toChars()); - arguments.setDim(i + 1); - (*arguments)[i] = arg; - nargs = i + 1; - done = 1; - } - - L1: - if (!(p.storageClass & STClazy && p.type.ty == Tvoid)) - { - bool isRef = (p.storageClass & (STCref | STCout)) != 0; - if (ubyte wm = arg.type.deduceWild(p.type, isRef)) - { - if (wildmatch) - wildmatch = MODmerge(wildmatch, wm); - else - wildmatch = wm; - //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); - } - } - } - if (done) - break; - } - if ((wildmatch == MODmutable || wildmatch == MODimmutable) && tf.next.hasWild() && (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf()))) - { - if (fd) - { - /* If the called function may return the reference to - * outer inout data, it should be rejected. - * - * void foo(ref inout(int) x) { - * ref inout(int) bar(inout(int)) { return x; } - * struct S { ref inout(int) bar() inout { return x; } } - * bar(int.init) = 1; // bad! - * S().bar() = 1; // bad! - * } - */ - Dsymbol s = null; - if (fd.isThis() || fd.isNested()) - s = fd.toParent2(); - for (; s; s = s.toParent2()) - { - if (auto ad = s.isAggregateDeclaration()) - { - if (ad.isNested()) - continue; - break; - } - if (auto ff = s.isFuncDeclaration()) - { - if ((cast(TypeFunction)ff.type).iswild) - goto Linouterr; - - if (ff.isNested() || ff.isThis()) - continue; - } - break; - } - } - else if (tf.isWild()) - { - Linouterr: - const(char)* s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch); - error(loc, "modify inout to %s is not allowed inside inout function", s); - return true; - } - } - - assert(nargs >= nparams); - for (size_t i = 0; i < nargs; i++) - { - Expression arg = (*arguments)[i]; - assert(arg); - if (i < nparams) - { - Parameter p = Parameter.getNth(tf.parameters, i); - if (!(p.storageClass & STClazy && p.type.ty == Tvoid)) - { - Type tprm = p.type; - if (p.type.hasWild()) - tprm = p.type.substWildTo(wildmatch); - if (!tprm.equals(arg.type)) - { - //printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars()); - arg = arg.implicitCastTo(sc, tprm); - arg = arg.optimize(WANTvalue, (p.storageClass & (STCref | STCout)) != 0); - } - } - if (p.storageClass & STCref) - { - arg = arg.toLvalue(sc, arg); - - // Look for mutable misaligned pointer, etc., in @safe mode - err |= checkUnsafeAccess(sc, arg, false, true); - } - else if (p.storageClass & STCout) - { - Type t = arg.type; - if (!t.isMutable() || !t.isAssignable()) // check blit assignable - { - arg.error("cannot modify struct %s with immutable members", arg.toChars()); - err = true; - } - else - { - // Look for misaligned pointer, etc., in @safe mode - err |= checkUnsafeAccess(sc, arg, false, true); - err |= checkDefCtor(arg.loc, t); // t must be default constructible - } - arg = arg.toLvalue(sc, arg); - } - else if (p.storageClass & STClazy) - { - // Convert lazy argument to a delegate - if (p.type.ty == Tvoid) - arg = toDelegate(arg, p.type, sc); - else - arg = toDelegate(arg, arg.type, sc); - } - //printf("arg: %s\n", arg.toChars()); - //printf("type: %s\n", arg.type.toChars()); - - if (tf.parameterEscapes(p)) - { - /* Argument value can escape from the called function. - * Check arg to see if it matters. - */ - if (global.params.vsafe) - err |= checkParamArgumentEscape(sc, fd, p.ident, arg, false); - } - else - { - /* Argument value cannot escape from the called function. - */ - Expression a = arg; - if (a.op == TOKcast) - a = (cast(CastExp)a).e1; - if (a.op == TOKfunction) - { - /* Function literals can only appear once, so if this - * appearance was scoped, there cannot be any others. - */ - FuncExp fe = cast(FuncExp)a; - fe.fd.tookAddressOf = 0; - } - else if (a.op == TOKdelegate) - { - /* For passing a delegate to a scoped parameter, - * this doesn't count as taking the address of it. - * We only worry about 'escaping' references to the function. - */ - DelegateExp de = cast(DelegateExp)a; - if (de.e1.op == TOKvar) - { - VarExp ve = cast(VarExp)de.e1; - FuncDeclaration f = ve.var.isFuncDeclaration(); - if (f) - { - f.tookAddressOf--; - //printf("--tookAddressOf = %d\n", f.tookAddressOf); - } - } - } - } - arg = arg.optimize(WANTvalue, (p.storageClass & (STCref | STCout)) != 0); - } - else - { - // These will be the trailing ... arguments - // If not D linkage, do promotions - if (tf.linkage != LINKd) - { - // Promote bytes, words, etc., to ints - arg = integralPromotions(arg, sc); - - // Promote floats to doubles - switch (arg.type.ty) - { - case Tfloat32: - arg = arg.castTo(sc, Type.tfloat64); - break; - - case Timaginary32: - arg = arg.castTo(sc, Type.timaginary64); - break; - - default: - break; - } - if (tf.varargs == 1) - { - const(char)* p = tf.linkage == LINKc ? "extern(C)" : "extern(C++)"; - if (arg.type.ty == Tarray) - { - arg.error("cannot pass dynamic arrays to %s vararg functions", p); - err = true; - } - if (arg.type.ty == Tsarray) - { - arg.error("cannot pass static arrays to %s vararg functions", p); - err = true; - } - } - } - - // Do not allow types that need destructors - if (arg.type.needsDestruction()) - { - arg.error("cannot pass types that need destruction as variadic arguments"); - err = true; - } - - // Convert static arrays to dynamic arrays - // BUG: I don't think this is right for D2 - Type tb = arg.type.toBasetype(); - if (tb.ty == Tsarray) - { - TypeSArray ts = cast(TypeSArray)tb; - Type ta = ts.next.arrayOf(); - if (ts.size(arg.loc) == 0) - arg = new NullExp(arg.loc, ta); - else - arg = arg.castTo(sc, ta); - } - if (tb.ty == Tstruct) - { - //arg = callCpCtor(sc, arg); - } - // Give error for overloaded function addresses - if (arg.op == TOKsymoff) - { - SymOffExp se = cast(SymOffExp)arg; - if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique()) - { - arg.error("function %s is overloaded", arg.toChars()); - err = true; - } - } - if (arg.checkValue()) - err = true; - arg = arg.optimize(WANTvalue); - } - (*arguments)[i] = arg; - } - - /* Remaining problems: - * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is - * implemented by calling a function) we'll defer this for now. - * 2. value structs (or static arrays of them) that need to be copy constructed - * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the - * function gets called (functions normally destroy their parameters) - * 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned - * up properly. Pushing arguments on the stack then cannot fail. - */ - if (1) - { - /* TODO: tackle problem 1) - */ - const bool leftToRight = true; // TODO: something like !fd.isArrayOp - if (!leftToRight) - assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity - - const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1); - const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1); - const ptrdiff_t step = (leftToRight ? 1 : -1); - - /* Compute indices of last throwing argument and first arg needing destruction. - * Used to not set up destructors unless an arg needs destruction on a throw - * in a later argument. - */ - ptrdiff_t lastthrow = -1; - ptrdiff_t firstdtor = -1; - for (ptrdiff_t i = start; i != end; i += step) - { - Expression arg = (*arguments)[i]; - if (canThrow(arg, sc.func, false)) - lastthrow = i; - if (firstdtor == -1 && arg.type.needsDestruction()) - { - Parameter p = (i >= nparams ? null : Parameter.getNth(tf.parameters, i)); - if (!(p && (p.storageClass & (STClazy | STCref | STCout)))) - firstdtor = i; - } - } - - /* Does problem 3) apply to this call? - */ - const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0 - && (lastthrow - firstdtor) * step > 0); - - /* If so, initialize 'eprefix' by declaring the gate - */ - VarDeclaration gate = null; - if (needsPrefix) - { - // eprefix => bool __gate [= false] - Identifier idtmp = Identifier.generateId("__gate"); - gate = new VarDeclaration(loc, Type.tbool, idtmp, null); - gate.storage_class |= STCtemp | STCctfe | STCvolatile; - gate.dsymbolSemantic(sc); - - auto ae = new DeclarationExp(loc, gate); - eprefix = ae.expressionSemantic(sc); - } - - for (ptrdiff_t i = start; i != end; i += step) - { - Expression arg = (*arguments)[i]; - - Parameter parameter = (i >= nparams ? null : Parameter.getNth(tf.parameters, i)); - const bool isRef = (parameter && (parameter.storageClass & (STCref | STCout))); - const bool isLazy = (parameter && (parameter.storageClass & STClazy)); - - /* Skip lazy parameters - */ - if (isLazy) - continue; - - /* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg. - * Declare a temporary variable for this arg and append that declaration to 'eprefix', - * which will implicitly take care of potential problem 2) for this arg. - * 'eprefix' will therefore finally contain all args up to and including the last - * potentially throwing arg, excluding all lazy parameters. - */ - if (gate) - { - const bool needsDtor = (!isRef && arg.type.needsDestruction() && i != lastthrow); - - /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor) - */ - auto tmp = copyToTemp(0, - needsDtor ? "__pfx" : "__pfy", - !isRef ? arg : arg.addressOf()); - tmp.dsymbolSemantic(sc); - - /* Modify the destructor so it only runs if gate==false, i.e., - * only if there was a throw while constructing the args - */ - if (!needsDtor) - { - if (tmp.edtor) - { - assert(i == lastthrow); - tmp.edtor = null; - } - } - else - { - // edtor => (__gate || edtor) - assert(tmp.edtor); - Expression e = tmp.edtor; - e = new LogicalExp(e.loc, TOKoror, new VarExp(e.loc, gate), e); - tmp.edtor = e.expressionSemantic(sc); - //printf("edtor: %s\n", tmp.edtor.toChars()); - } - - // eprefix => (eprefix, auto __pfx/y = arg) - auto ae = new DeclarationExp(loc, tmp); - eprefix = Expression.combine(eprefix, ae.expressionSemantic(sc)); - - // arg => __pfx/y - arg = new VarExp(loc, tmp); - arg = arg.expressionSemantic(sc); - if (isRef) - { - arg = new PtrExp(loc, arg); - arg = arg.expressionSemantic(sc); - } - - /* Last throwing arg? Then finalize eprefix => (eprefix, gate = true), - * i.e., disable the dtors right after constructing the last throwing arg. - * From now on, the callee will take care of destructing the args because - * the args are implicitly moved into function parameters. - * - * Set gate to null to let the next iterations know they don't need to - * append to eprefix anymore. - */ - if (i == lastthrow) - { - auto e = new AssignExp(gate.loc, new VarExp(gate.loc, gate), new IntegerExp(gate.loc, 1, Type.tbool)); - eprefix = Expression.combine(eprefix, e.expressionSemantic(sc)); - gate = null; - } - } - else - { - /* No gate, no prefix to append to. - * Handle problem 2) by calling the copy constructor for value structs - * (or static arrays of them) if appropriate. - */ - Type tv = arg.type.baseElemOf(); - if (!isRef && tv.ty == Tstruct) - arg = doCopyOrMove(sc, arg); - } - - (*arguments)[i] = arg; - } - } - //if (eprefix) printf("eprefix: %s\n", eprefix.toChars()); - - // If D linkage and variadic, add _arguments[] as first argument - if (tf.linkage == LINKd && tf.varargs == 1) - { - assert(arguments.dim >= nparams); - - auto args = new Parameters(); - args.setDim(arguments.dim - nparams); - for (size_t i = 0; i < arguments.dim - nparams; i++) - { - auto arg = new Parameter(STCin, (*arguments)[nparams + i].type, null, null); - (*args)[i] = arg; - } - auto tup = new TypeTuple(args); - Expression e = new TypeidExp(loc, tup); - e = e.expressionSemantic(sc); - arguments.insert(0, e); - } - - Type tret = tf.next; - if (isCtorCall) - { - //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd.toChars(), fd.type.toChars(), - // wildmatch, tf.isWild(), fd.isReturnIsolated()); - if (!tthis) - { - assert(sc.intypeof || global.errors); - tthis = fd.isThis().type.addMod(fd.type.mod); - } - if (tf.isWild() && !fd.isReturnIsolated()) - { - if (wildmatch) - tret = tret.substWildTo(wildmatch); - int offset; - if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0)) - { - const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars(); - const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars(); - .error(loc, "inout constructor %s creates%s object, not%s", fd.toPrettyChars(), s1, s2); - err = true; - } - } - tret = tthis; - } - else if (wildmatch && tret) - { - /* Adjust function return type based on wildmatch - */ - //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret.toChars()); - tret = tret.substWildTo(wildmatch); - } - *prettype = tret; - *peprefix = eprefix; - return (err || olderrors != global.errors); -} - /****************************************************************/ /* A type meant as a union of all the Expression types, * to serve essentially as a Variant that will sit on the stack diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index b5f0052dfeff..178994aa7ec3 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -21,6 +21,7 @@ import ddmd.arrayop; import ddmd.arraytypes; import ddmd.attrib; import ddmd.astcodegen; +import ddmd.canthrow; import ddmd.dscope; import ddmd.dsymbol; import ddmd.declaration; @@ -43,6 +44,7 @@ import ddmd.hdrgen; import ddmd.id; import ddmd.identifier; import ddmd.imphint; +import ddmd.inline; import ddmd.intrange; import ddmd.mtype; import ddmd.nspace; @@ -479,6 +481,647 @@ private extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* return err; } +/**************************************** + * Now that we know the exact type of the function we're calling, + * the arguments[] need to be adjusted: + * 1. implicitly convert argument to the corresponding parameter type + * 2. add default arguments for any missing arguments + * 3. do default promotions on arguments corresponding to ... + * 4. add hidden _arguments[] argument + * 5. call copy constructor for struct value arguments + * Input: + * tf type of the function + * fd the function being called, NULL if called indirectly + * Output: + * *prettype return type of function + * *peprefix expression to execute before arguments[] are evaluated, NULL if none + * Returns: + * true errors happened + */ +private extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix) +{ + //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); + assert(arguments); + assert(fd || tf.next); + size_t nargs = arguments ? arguments.dim : 0; + size_t nparams = Parameter.dim(tf.parameters); + uint olderrors = global.errors; + bool err = false; + *prettype = Type.terror; + Expression eprefix = null; + *peprefix = null; + + if (nargs > nparams && tf.varargs == 0) + { + error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", cast(ulong)nparams, cast(ulong)nargs, tf.toChars()); + return true; + } + + // If inferring return type, and semantic3() needs to be run if not already run + if (!tf.next && fd.inferRetType) + { + fd.functionSemantic(); + } + else if (fd && fd.parent) + { + TemplateInstance ti = fd.parent.isTemplateInstance(); + if (ti && ti.tempdecl) + { + fd.functionSemantic3(); + } + } + bool isCtorCall = fd && fd.needThis() && fd.isCtorDeclaration(); + + size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) + + /* If the function return type has wildcards in it, we'll need to figure out the actual type + * based on the actual argument types. + */ + MOD wildmatch = 0; + if (tthis && tf.isWild() && !isCtorCall) + { + Type t = tthis; + if (t.isImmutable()) + wildmatch = MODimmutable; + else if (t.isWildConst()) + wildmatch = MODwildconst; + else if (t.isWild()) + wildmatch = MODwild; + else if (t.isConst()) + wildmatch = MODconst; + else + wildmatch = MODmutable; + } + + int done = 0; + for (size_t i = 0; i < n; i++) + { + Expression arg; + + if (i < nargs) + arg = (*arguments)[i]; + else + arg = null; + + if (i < nparams) + { + Parameter p = Parameter.getNth(tf.parameters, i); + + if (!arg) + { + if (!p.defaultArg) + { + if (tf.varargs == 2 && i + 1 == nparams) + goto L2; + error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); + return true; + } + arg = p.defaultArg; + arg = inlineCopy(arg, sc); + // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ + arg = arg.resolveLoc(loc, sc); + arguments.push(arg); + nargs++; + } + + if (tf.varargs == 2 && i + 1 == nparams) + { + //printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars()); + { + MATCH m; + if ((m = arg.implicitConvTo(p.type)) > MATCH.nomatch) + { + if (p.type.nextOf() && arg.implicitConvTo(p.type.nextOf()) >= m) + goto L2; + else if (nargs != nparams) + { + error(loc, "expected %llu function arguments, not %llu", cast(ulong)nparams, cast(ulong)nargs); + return true; + } + goto L1; + } + } + L2: + Type tb = p.type.toBasetype(); + Type tret = p.isLazyArray(); + switch (tb.ty) + { + case Tsarray: + case Tarray: + { + /* Create a static array variable v of type arg.type: + * T[dim] __arrayArg = [ arguments[i], ..., arguments[nargs-1] ]; + * + * The array literal in the initializer of the hidden variable + * is now optimized. + * https://issues.dlang.org/show_bug.cgi?id=2356 + */ + Type tbn = (cast(TypeArray)tb).next; + Type tsa = tbn.sarrayOf(nargs - i); + + auto elements = new Expressions(); + elements.setDim(nargs - i); + for (size_t u = 0; u < elements.dim; u++) + { + Expression a = (*arguments)[i + u]; + if (tret && a.implicitConvTo(tret)) + { + a = a.implicitCastTo(sc, tret); + a = a.optimize(WANTvalue); + a = toDelegate(a, a.type, sc); + } + else + a = a.implicitCastTo(sc, tbn); + (*elements)[u] = a; + } + // https://issues.dlang.org/show_bug.cgi?id=14395 + // Convert to a static array literal, or its slice. + arg = new ArrayLiteralExp(loc, elements); + arg.type = tsa; + if (tb.ty == Tarray) + { + arg = new SliceExp(loc, arg, null, null); + arg.type = p.type; + } + break; + } + case Tclass: + { + /* Set arg to be: + * new Tclass(arg0, arg1, ..., argn) + */ + auto args = new Expressions(); + args.setDim(nargs - i); + for (size_t u = i; u < nargs; u++) + (*args)[u - i] = (*arguments)[u]; + arg = new NewExp(loc, null, null, p.type, args); + break; + } + default: + if (!arg) + { + error(loc, "not enough arguments"); + return true; + } + break; + } + arg = arg.expressionSemantic(sc); + //printf("\targ = '%s'\n", arg.toChars()); + arguments.setDim(i + 1); + (*arguments)[i] = arg; + nargs = i + 1; + done = 1; + } + + L1: + if (!(p.storageClass & STClazy && p.type.ty == Tvoid)) + { + bool isRef = (p.storageClass & (STCref | STCout)) != 0; + if (ubyte wm = arg.type.deduceWild(p.type, isRef)) + { + if (wildmatch) + wildmatch = MODmerge(wildmatch, wm); + else + wildmatch = wm; + //printf("[%d] p = %s, a = %s, wm = %d, wildmatch = %d\n", i, p.type.toChars(), arg.type.toChars(), wm, wildmatch); + } + } + } + if (done) + break; + } + if ((wildmatch == MODmutable || wildmatch == MODimmutable) && tf.next.hasWild() && (tf.isref || !tf.next.implicitConvTo(tf.next.immutableOf()))) + { + if (fd) + { + /* If the called function may return the reference to + * outer inout data, it should be rejected. + * + * void foo(ref inout(int) x) { + * ref inout(int) bar(inout(int)) { return x; } + * struct S { ref inout(int) bar() inout { return x; } } + * bar(int.init) = 1; // bad! + * S().bar() = 1; // bad! + * } + */ + Dsymbol s = null; + if (fd.isThis() || fd.isNested()) + s = fd.toParent2(); + for (; s; s = s.toParent2()) + { + if (auto ad = s.isAggregateDeclaration()) + { + if (ad.isNested()) + continue; + break; + } + if (auto ff = s.isFuncDeclaration()) + { + if ((cast(TypeFunction)ff.type).iswild) + goto Linouterr; + + if (ff.isNested() || ff.isThis()) + continue; + } + break; + } + } + else if (tf.isWild()) + { + Linouterr: + const(char)* s = wildmatch == MODmutable ? "mutable" : MODtoChars(wildmatch); + error(loc, "modify inout to %s is not allowed inside inout function", s); + return true; + } + } + + assert(nargs >= nparams); + for (size_t i = 0; i < nargs; i++) + { + Expression arg = (*arguments)[i]; + assert(arg); + if (i < nparams) + { + Parameter p = Parameter.getNth(tf.parameters, i); + if (!(p.storageClass & STClazy && p.type.ty == Tvoid)) + { + Type tprm = p.type; + if (p.type.hasWild()) + tprm = p.type.substWildTo(wildmatch); + if (!tprm.equals(arg.type)) + { + //printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars()); + arg = arg.implicitCastTo(sc, tprm); + arg = arg.optimize(WANTvalue, (p.storageClass & (STCref | STCout)) != 0); + } + } + if (p.storageClass & STCref) + { + arg = arg.toLvalue(sc, arg); + + // Look for mutable misaligned pointer, etc., in @safe mode + err |= checkUnsafeAccess(sc, arg, false, true); + } + else if (p.storageClass & STCout) + { + Type t = arg.type; + if (!t.isMutable() || !t.isAssignable()) // check blit assignable + { + arg.error("cannot modify struct %s with immutable members", arg.toChars()); + err = true; + } + else + { + // Look for misaligned pointer, etc., in @safe mode + err |= checkUnsafeAccess(sc, arg, false, true); + err |= checkDefCtor(arg.loc, t); // t must be default constructible + } + arg = arg.toLvalue(sc, arg); + } + else if (p.storageClass & STClazy) + { + // Convert lazy argument to a delegate + if (p.type.ty == Tvoid) + arg = toDelegate(arg, p.type, sc); + else + arg = toDelegate(arg, arg.type, sc); + } + //printf("arg: %s\n", arg.toChars()); + //printf("type: %s\n", arg.type.toChars()); + + if (tf.parameterEscapes(p)) + { + /* Argument value can escape from the called function. + * Check arg to see if it matters. + */ + if (global.params.vsafe) + err |= checkParamArgumentEscape(sc, fd, p.ident, arg, false); + } + else + { + /* Argument value cannot escape from the called function. + */ + Expression a = arg; + if (a.op == TOKcast) + a = (cast(CastExp)a).e1; + if (a.op == TOKfunction) + { + /* Function literals can only appear once, so if this + * appearance was scoped, there cannot be any others. + */ + FuncExp fe = cast(FuncExp)a; + fe.fd.tookAddressOf = 0; + } + else if (a.op == TOKdelegate) + { + /* For passing a delegate to a scoped parameter, + * this doesn't count as taking the address of it. + * We only worry about 'escaping' references to the function. + */ + DelegateExp de = cast(DelegateExp)a; + if (de.e1.op == TOKvar) + { + VarExp ve = cast(VarExp)de.e1; + FuncDeclaration f = ve.var.isFuncDeclaration(); + if (f) + { + f.tookAddressOf--; + //printf("--tookAddressOf = %d\n", f.tookAddressOf); + } + } + } + } + arg = arg.optimize(WANTvalue, (p.storageClass & (STCref | STCout)) != 0); + } + else + { + // These will be the trailing ... arguments + // If not D linkage, do promotions + if (tf.linkage != LINKd) + { + // Promote bytes, words, etc., to ints + arg = integralPromotions(arg, sc); + + // Promote floats to doubles + switch (arg.type.ty) + { + case Tfloat32: + arg = arg.castTo(sc, Type.tfloat64); + break; + + case Timaginary32: + arg = arg.castTo(sc, Type.timaginary64); + break; + + default: + break; + } + if (tf.varargs == 1) + { + const(char)* p = tf.linkage == LINKc ? "extern(C)" : "extern(C++)"; + if (arg.type.ty == Tarray) + { + arg.error("cannot pass dynamic arrays to %s vararg functions", p); + err = true; + } + if (arg.type.ty == Tsarray) + { + arg.error("cannot pass static arrays to %s vararg functions", p); + err = true; + } + } + } + + // Do not allow types that need destructors + if (arg.type.needsDestruction()) + { + arg.error("cannot pass types that need destruction as variadic arguments"); + err = true; + } + + // Convert static arrays to dynamic arrays + // BUG: I don't think this is right for D2 + Type tb = arg.type.toBasetype(); + if (tb.ty == Tsarray) + { + TypeSArray ts = cast(TypeSArray)tb; + Type ta = ts.next.arrayOf(); + if (ts.size(arg.loc) == 0) + arg = new NullExp(arg.loc, ta); + else + arg = arg.castTo(sc, ta); + } + if (tb.ty == Tstruct) + { + //arg = callCpCtor(sc, arg); + } + // Give error for overloaded function addresses + if (arg.op == TOKsymoff) + { + SymOffExp se = cast(SymOffExp)arg; + if (se.hasOverloads && !se.var.isFuncDeclaration().isUnique()) + { + arg.error("function %s is overloaded", arg.toChars()); + err = true; + } + } + if (arg.checkValue()) + err = true; + arg = arg.optimize(WANTvalue); + } + (*arguments)[i] = arg; + } + + /* Remaining problems: + * 1. order of evaluation - some function push L-to-R, others R-to-L. Until we resolve what array assignment does (which is + * implemented by calling a function) we'll defer this for now. + * 2. value structs (or static arrays of them) that need to be copy constructed + * 3. value structs (or static arrays of them) that have destructors, and subsequent arguments that may throw before the + * function gets called (functions normally destroy their parameters) + * 2 and 3 are handled by doing the argument construction in 'eprefix' so that if a later argument throws, they are cleaned + * up properly. Pushing arguments on the stack then cannot fail. + */ + if (1) + { + /* TODO: tackle problem 1) + */ + const bool leftToRight = true; // TODO: something like !fd.isArrayOp + if (!leftToRight) + assert(nargs == nparams); // no variadics for RTL order, as they would probably be evaluated LTR and so add complexity + + const ptrdiff_t start = (leftToRight ? 0 : cast(ptrdiff_t)nargs - 1); + const ptrdiff_t end = (leftToRight ? cast(ptrdiff_t)nargs : -1); + const ptrdiff_t step = (leftToRight ? 1 : -1); + + /* Compute indices of last throwing argument and first arg needing destruction. + * Used to not set up destructors unless an arg needs destruction on a throw + * in a later argument. + */ + ptrdiff_t lastthrow = -1; + ptrdiff_t firstdtor = -1; + for (ptrdiff_t i = start; i != end; i += step) + { + Expression arg = (*arguments)[i]; + if (canThrow(arg, sc.func, false)) + lastthrow = i; + if (firstdtor == -1 && arg.type.needsDestruction()) + { + Parameter p = (i >= nparams ? null : Parameter.getNth(tf.parameters, i)); + if (!(p && (p.storageClass & (STClazy | STCref | STCout)))) + firstdtor = i; + } + } + + /* Does problem 3) apply to this call? + */ + const bool needsPrefix = (firstdtor >= 0 && lastthrow >= 0 + && (lastthrow - firstdtor) * step > 0); + + /* If so, initialize 'eprefix' by declaring the gate + */ + VarDeclaration gate = null; + if (needsPrefix) + { + // eprefix => bool __gate [= false] + Identifier idtmp = Identifier.generateId("__gate"); + gate = new VarDeclaration(loc, Type.tbool, idtmp, null); + gate.storage_class |= STCtemp | STCctfe | STCvolatile; + gate.dsymbolSemantic(sc); + + auto ae = new DeclarationExp(loc, gate); + eprefix = ae.expressionSemantic(sc); + } + + for (ptrdiff_t i = start; i != end; i += step) + { + Expression arg = (*arguments)[i]; + + Parameter parameter = (i >= nparams ? null : Parameter.getNth(tf.parameters, i)); + const bool isRef = (parameter && (parameter.storageClass & (STCref | STCout))); + const bool isLazy = (parameter && (parameter.storageClass & STClazy)); + + /* Skip lazy parameters + */ + if (isLazy) + continue; + + /* Do we have a gate? Then we have a prefix and we're not yet past the last throwing arg. + * Declare a temporary variable for this arg and append that declaration to 'eprefix', + * which will implicitly take care of potential problem 2) for this arg. + * 'eprefix' will therefore finally contain all args up to and including the last + * potentially throwing arg, excluding all lazy parameters. + */ + if (gate) + { + const bool needsDtor = (!isRef && arg.type.needsDestruction() && i != lastthrow); + + /* Declare temporary 'auto __pfx = arg' (needsDtor) or 'auto __pfy = arg' (!needsDtor) + */ + auto tmp = copyToTemp(0, + needsDtor ? "__pfx" : "__pfy", + !isRef ? arg : arg.addressOf()); + tmp.dsymbolSemantic(sc); + + /* Modify the destructor so it only runs if gate==false, i.e., + * only if there was a throw while constructing the args + */ + if (!needsDtor) + { + if (tmp.edtor) + { + assert(i == lastthrow); + tmp.edtor = null; + } + } + else + { + // edtor => (__gate || edtor) + assert(tmp.edtor); + Expression e = tmp.edtor; + e = new LogicalExp(e.loc, TOKoror, new VarExp(e.loc, gate), e); + tmp.edtor = e.expressionSemantic(sc); + //printf("edtor: %s\n", tmp.edtor.toChars()); + } + + // eprefix => (eprefix, auto __pfx/y = arg) + auto ae = new DeclarationExp(loc, tmp); + eprefix = Expression.combine(eprefix, ae.expressionSemantic(sc)); + + // arg => __pfx/y + arg = new VarExp(loc, tmp); + arg = arg.expressionSemantic(sc); + if (isRef) + { + arg = new PtrExp(loc, arg); + arg = arg.expressionSemantic(sc); + } + + /* Last throwing arg? Then finalize eprefix => (eprefix, gate = true), + * i.e., disable the dtors right after constructing the last throwing arg. + * From now on, the callee will take care of destructing the args because + * the args are implicitly moved into function parameters. + * + * Set gate to null to let the next iterations know they don't need to + * append to eprefix anymore. + */ + if (i == lastthrow) + { + auto e = new AssignExp(gate.loc, new VarExp(gate.loc, gate), new IntegerExp(gate.loc, 1, Type.tbool)); + eprefix = Expression.combine(eprefix, e.expressionSemantic(sc)); + gate = null; + } + } + else + { + /* No gate, no prefix to append to. + * Handle problem 2) by calling the copy constructor for value structs + * (or static arrays of them) if appropriate. + */ + Type tv = arg.type.baseElemOf(); + if (!isRef && tv.ty == Tstruct) + arg = doCopyOrMove(sc, arg); + } + + (*arguments)[i] = arg; + } + } + //if (eprefix) printf("eprefix: %s\n", eprefix.toChars()); + + // If D linkage and variadic, add _arguments[] as first argument + if (tf.linkage == LINKd && tf.varargs == 1) + { + assert(arguments.dim >= nparams); + + auto args = new Parameters(); + args.setDim(arguments.dim - nparams); + for (size_t i = 0; i < arguments.dim - nparams; i++) + { + auto arg = new Parameter(STCin, (*arguments)[nparams + i].type, null, null); + (*args)[i] = arg; + } + auto tup = new TypeTuple(args); + Expression e = new TypeidExp(loc, tup); + e = e.expressionSemantic(sc); + arguments.insert(0, e); + } + + Type tret = tf.next; + if (isCtorCall) + { + //printf("[%s] fd = %s %s, %d %d %d\n", loc.toChars(), fd.toChars(), fd.type.toChars(), + // wildmatch, tf.isWild(), fd.isReturnIsolated()); + if (!tthis) + { + assert(sc.intypeof || global.errors); + tthis = fd.isThis().type.addMod(fd.type.mod); + } + if (tf.isWild() && !fd.isReturnIsolated()) + { + if (wildmatch) + tret = tret.substWildTo(wildmatch); + int offset; + if (!tret.implicitConvTo(tthis) && !(MODimplicitConv(tret.mod, tthis.mod) && tret.isBaseOf(tthis, &offset) && offset == 0)) + { + const(char)* s1 = tret.isNaked() ? " mutable" : tret.modToChars(); + const(char)* s2 = tthis.isNaked() ? " mutable" : tthis.modToChars(); + .error(loc, "inout constructor %s creates%s object, not%s", fd.toPrettyChars(), s1, s2); + err = true; + } + } + tret = tthis; + } + else if (wildmatch && tret) + { + /* Adjust function return type based on wildmatch + */ + //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret.toChars()); + tret = tret.substWildTo(wildmatch); + } + *prettype = tret; + *peprefix = eprefix; + return (err || olderrors != global.errors); +} + private extern (C++) final class ExpressionSemanticVisitor : Visitor { alias visit = super.visit; From 7d8055d5b26dd44ec8d3c9dbf7f1f691e3a984c1 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:44:08 +0300 Subject: [PATCH 08/13] Move checkDefCtor to expressionsem and make it private --- src/ddmd/expression.d | 21 --------------------- src/ddmd/expressionsem.d | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 570557d751e9..73ee533be4e0 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1120,27 +1120,6 @@ extern (C++) Expression valueNoDtor(Expression e) return e; } -/******************************************** - * Issue an error if default construction is disabled for type t. - * Default construction is required for arrays and 'out' parameters. - * Returns: - * true an error was issued - */ -extern (C++) bool checkDefCtor(Loc loc, Type t) -{ - t = t.baseElemOf(); - if (t.ty == Tstruct) - { - StructDeclaration sd = (cast(TypeStruct)t).sym; - if (sd.noDefaultCtor) - { - sd.error(loc, "default construction is disabled"); - return true; - } - } - return false; -} - /********************************************* * If e is an instance of a struct, and that struct has a copy constructor, * rewrite e as: diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index 178994aa7ec3..91962a6420a7 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -481,6 +481,27 @@ private extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* return err; } +/******************************************** + * Issue an error if default construction is disabled for type t. + * Default construction is required for arrays and 'out' parameters. + * Returns: + * true an error was issued + */ +private extern (C++) bool checkDefCtor(Loc loc, Type t) +{ + t = t.baseElemOf(); + if (t.ty == Tstruct) + { + StructDeclaration sd = (cast(TypeStruct)t).sym; + if (sd.noDefaultCtor) + { + sd.error(loc, "default construction is disabled"); + return true; + } + } + return false; +} + /**************************************** * Now that we know the exact type of the function we're calling, * the arguments[] need to be adjusted: From 2df450d7f40f39ec2ad7397917caf11142c972d9 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:48:31 +0300 Subject: [PATCH 09/13] Move mofifyFieldVar to expressionsem and make it private --- src/ddmd/declaration.d | 107 +++++++++++++++++++++++++++++++++++++++++ src/ddmd/expression.d | 107 ----------------------------------------- 2 files changed, 107 insertions(+), 107 deletions(-) diff --git a/src/ddmd/declaration.d b/src/ddmd/declaration.d index bd0a25dfec9c..448a51be7409 100644 --- a/src/ddmd/declaration.d +++ b/src/ddmd/declaration.d @@ -72,6 +72,113 @@ extern (C++) bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, return result; } +/*********************************************** + * Mark variable v as modified if it is inside a constructor that var + * is a field in. + */ +private extern (C++) int modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) +{ + //printf("modifyFieldVar(var = %s)\n", var.toChars()); + Dsymbol s = sc.func; + while (1) + { + FuncDeclaration fd = null; + if (s) + fd = s.isFuncDeclaration(); + if (fd && + ((fd.isCtorDeclaration() && var.isField()) || + (fd.isStaticCtorDeclaration() && !var.isField())) && + fd.toParent2() == var.toParent2() && + (!e1 || e1.op == TOKthis)) + { + bool result = true; + + var.ctorinit = true; + //printf("setting ctorinit\n"); + + if (var.isField() && sc.fieldinit && !sc.intypeof) + { + assert(e1); + auto mustInit = ((var.storage_class & STCnodefaultctor) != 0 || + var.type.needsNested()); + + auto dim = sc.fieldinit_dim; + auto ad = fd.isMember2(); + assert(ad); + size_t i; + for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? + { + if (ad.fields[i] == var) + break; + } + assert(i < dim); + uint fi = sc.fieldinit[i]; + + if (fi & CSXthis_ctor) + { + if (var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field '%s' initialized multiple times", modStr, var.toChars()); + } + } + else if (sc.noctor || (fi & CSXlabel)) + { + if (!mustInit && var.type.isMutable() && e1.type.isMutable()) + result = false; + else + { + const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); + .error(loc, "%s field '%s' initialization is not allowed in loops or after labels", modStr, var.toChars()); + } + } + + sc.fieldinit[i] |= CSXthis_ctor; + if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 + { + foreach (j, v; ad.fields) + { + if (v is var || !var.isOverlappedWith(v)) + continue; + v.ctorinit = true; + sc.fieldinit[j] = CSXthis_ctor; + } + } + } + else if (fd != sc.func) + { + if (var.type.isMutable()) + result = false; + else if (sc.func.fes) + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s '%s' initialization is not allowed in foreach loop", + MODtoChars(var.type.mod), p, var.toChars()); + } + else + { + const(char)* p = var.isField() ? "field" : var.kind(); + .error(loc, "%s %s '%s' initialization is not allowed in nested function '%s'", + MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); + } + } + return result; + } + else + { + if (s) + { + s = s.toParent2(); + continue; + } + } + break; + } + return false; +} + /****************************************** */ extern (C++) void ObjectNotFound(Identifier id) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 73ee533be4e0..4fa42b8d3301 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1259,113 +1259,6 @@ extern (C++) DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident) return new DotIdExp(loc, new TypeExp(loc, type), ident); } -/*********************************************** - * Mark variable v as modified if it is inside a constructor that var - * is a field in. - */ -extern (C++) int modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) -{ - //printf("modifyFieldVar(var = %s)\n", var.toChars()); - Dsymbol s = sc.func; - while (1) - { - FuncDeclaration fd = null; - if (s) - fd = s.isFuncDeclaration(); - if (fd && - ((fd.isCtorDeclaration() && var.isField()) || - (fd.isStaticCtorDeclaration() && !var.isField())) && - fd.toParent2() == var.toParent2() && - (!e1 || e1.op == TOKthis)) - { - bool result = true; - - var.ctorinit = true; - //printf("setting ctorinit\n"); - - if (var.isField() && sc.fieldinit && !sc.intypeof) - { - assert(e1); - auto mustInit = ((var.storage_class & STCnodefaultctor) != 0 || - var.type.needsNested()); - - auto dim = sc.fieldinit_dim; - auto ad = fd.isMember2(); - assert(ad); - size_t i; - for (i = 0; i < dim; i++) // same as findFieldIndexByName in ctfeexp.c ? - { - if (ad.fields[i] == var) - break; - } - assert(i < dim); - uint fi = sc.fieldinit[i]; - - if (fi & CSXthis_ctor) - { - if (var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field '%s' initialized multiple times", modStr, var.toChars()); - } - } - else if (sc.noctor || (fi & CSXlabel)) - { - if (!mustInit && var.type.isMutable() && e1.type.isMutable()) - result = false; - else - { - const(char)* modStr = !var.type.isMutable() ? MODtoChars(var.type.mod) : MODtoChars(e1.type.mod); - .error(loc, "%s field '%s' initialization is not allowed in loops or after labels", modStr, var.toChars()); - } - } - - sc.fieldinit[i] |= CSXthis_ctor; - if (var.overlapped) // https://issues.dlang.org/show_bug.cgi?id=15258 - { - foreach (j, v; ad.fields) - { - if (v is var || !var.isOverlappedWith(v)) - continue; - v.ctorinit = true; - sc.fieldinit[j] = CSXthis_ctor; - } - } - } - else if (fd != sc.func) - { - if (var.type.isMutable()) - result = false; - else if (sc.func.fes) - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s '%s' initialization is not allowed in foreach loop", - MODtoChars(var.type.mod), p, var.toChars()); - } - else - { - const(char)* p = var.isField() ? "field" : var.kind(); - .error(loc, "%s %s '%s' initialization is not allowed in nested function '%s'", - MODtoChars(var.type.mod), p, var.toChars(), sc.func.toChars()); - } - } - return result; - } - else - { - if (s) - { - s = s.toParent2(); - continue; - } - } - break; - } - return false; -} - extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Expression e2) { Expression e; From e7a22596126b84fc28a12ccd3c8095d57c6c5acb Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:49:05 +0300 Subject: [PATCH 10/13] Make opAssignToOp private --- src/ddmd/expression.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 4fa42b8d3301..9a8817d36f54 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1259,7 +1259,7 @@ extern (C++) DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident) return new DotIdExp(loc, new TypeExp(loc, type), ident); } -extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Expression e2) +private extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Expression e2) { Expression e; switch (op) From 433872c21626a664bd14bc3e41c5b77f42f0e3a5 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 13:49:40 +0300 Subject: [PATCH 11/13] Make extractOpDollarSideEffect private --- src/ddmd/expression.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 9a8817d36f54..ddc9e950aa77 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -1316,7 +1316,7 @@ private extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Exp /****************************************************************/ -extern (C++) Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) +private extern (C++) Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) { Expression e0; Expression e1 = Expression.extractLast(ue.e1, &e0); From 7ea63e0c5043324ea98d05ec1161ce895cda7bc8 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 14:10:08 +0300 Subject: [PATCH 12/13] Move loadStdMath to expressionsem and make it private --- src/ddmd/expression.d | 19 ------------------- src/ddmd/expressionsem.d | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index ddc9e950aa77..058cbb0f5764 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -6672,25 +6672,6 @@ extern (C++) final class PowExp : BinExp } } -extern (C++) Module loadStdMath() -{ - static __gshared Import impStdMath = null; - if (!impStdMath) - { - auto a = new Identifiers(); - a.push(Id.std); - auto s = new Import(Loc(), a, Id.math, null, false); - s.load(null); - if (s.mod) - { - s.mod.importAll(null); - s.mod.dsymbolSemantic(null); - } - impStdMath = s; - } - return impStdMath.mod; -} - /*********************************************************** */ extern (C++) final class ShlExp : BinExp diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index 91962a6420a7..c60963e1e5b6 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -1143,6 +1143,25 @@ private extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf return (err || olderrors != global.errors); } +private extern (C++) Module loadStdMath() +{ + static __gshared Import impStdMath = null; + if (!impStdMath) + { + auto a = new Identifiers(); + a.push(Id.std); + auto s = new Import(Loc(), a, Id.math, null, false); + s.load(null); + if (s.mod) + { + s.mod.importAll(null); + s.mod.dsymbolSemantic(null); + } + impStdMath = s; + } + return impStdMath.mod; +} + private extern (C++) final class ExpressionSemanticVisitor : Visitor { alias visit = super.visit; From a4d2298efe9d9e85d3c1fe92b5202ffead0cac1b Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Fri, 27 Oct 2017 15:40:43 +0300 Subject: [PATCH 13/13] Remove extern (C++) from private methods --- src/ddmd/declaration.d | 2 +- src/ddmd/expression.d | 10 +++++----- src/ddmd/expressionsem.d | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ddmd/declaration.d b/src/ddmd/declaration.d index 448a51be7409..4bb810a0fe2c 100644 --- a/src/ddmd/declaration.d +++ b/src/ddmd/declaration.d @@ -76,7 +76,7 @@ extern (C++) bool checkFrameAccess(Loc loc, Scope* sc, AggregateDeclaration ad, * Mark variable v as modified if it is inside a constructor that var * is a field in. */ -private extern (C++) int modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) +private int modifyFieldVar(Loc loc, Scope* sc, VarDeclaration var, Expression e1) { //printf("modifyFieldVar(var = %s)\n", var.toChars()); Dsymbol s = sc.func; diff --git a/src/ddmd/expression.d b/src/ddmd/expression.d index 058cbb0f5764..3466ff3a9a79 100644 --- a/src/ddmd/expression.d +++ b/src/ddmd/expression.d @@ -457,7 +457,7 @@ extern (C++) bool isNeedThisScope(Scope* sc, Declaration d) * Bugs: * This doesn't appear to do anything. */ -private extern (C++) bool checkPropertyCall(Expression e, Expression emsg) +private bool checkPropertyCall(Expression e, Expression emsg) { while (e.op == TOKcomma) e = (cast(CommaExp)e).e2; @@ -586,7 +586,7 @@ extern (C++) Expression resolvePropertiesOnly(Scope* sc, Expression e1) /****************************** * Find symbol in accordance with the UFCS name look up rule */ -private extern (C++) Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) +private Expression searchUFCS(Scope* sc, UnaExp ue, Identifier ident) { //printf("searchUFCS(ident = %s)\n", ident.toChars()); Loc loc = ue.loc; @@ -1127,7 +1127,7 @@ extern (C++) Expression valueNoDtor(Expression e) * Input: * sc just used to specify the scope of created temporary variable */ -private extern (C++) Expression callCpCtor(Scope* sc, Expression e) +private Expression callCpCtor(Scope* sc, Expression e) { Type tv = e.type.baseElemOf(); if (tv.ty == Tstruct) @@ -1259,7 +1259,7 @@ extern (C++) DotIdExp typeDotIdExp(Loc loc, Type type, Identifier ident) return new DotIdExp(loc, new TypeExp(loc, type), ident); } -private extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Expression e2) +private Expression opAssignToOp(Loc loc, TOK op, Expression e1, Expression e2) { Expression e; switch (op) @@ -1316,7 +1316,7 @@ private extern (C++) Expression opAssignToOp(Loc loc, TOK op, Expression e1, Exp /****************************************************************/ -private extern (C++) Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) +private Expression extractOpDollarSideEffect(Scope* sc, UnaExp ue) { Expression e0; Expression e1 = Expression.extractLast(ue.e1, &e0); diff --git a/src/ddmd/expressionsem.d b/src/ddmd/expressionsem.d index c60963e1e5b6..04c7c2b2fc8a 100644 --- a/src/ddmd/expressionsem.d +++ b/src/ddmd/expressionsem.d @@ -73,7 +73,7 @@ enum LOGSEMANTIC = false; /*************************************** * Pull out any properties. */ -private extern (C++) Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) +private Expression resolvePropertiesX(Scope* sc, Expression e1, Expression e2 = null) { //printf("resolvePropertiesX, e1 = %s %s, e2 = %s\n", Token.toChars(e1.op), e1.toChars(), e2 ? e2.toChars() : null); Loc loc = e1.loc; @@ -341,7 +341,7 @@ extern (C++) Expression resolveProperties(Scope* sc, Expression e) * Returns: * true a semantic error was detected */ -private extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* exps, Type* pt) +private bool arrayExpressionToCommonType(Scope* sc, Expressions* exps, Type* pt) { /* Still have a problem with: * ubyte[][] = [ cast(ubyte[])"hello", [1]]; @@ -453,7 +453,7 @@ private extern (C++) bool arrayExpressionToCommonType(Scope* sc, Expressions* ex * Returns: * true a semantic error occurred */ -private extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* exps) +private bool preFunctionParameters(Loc loc, Scope* sc, Expressions* exps) { bool err = false; if (exps) @@ -487,7 +487,7 @@ private extern (C++) bool preFunctionParameters(Loc loc, Scope* sc, Expressions* * Returns: * true an error was issued */ -private extern (C++) bool checkDefCtor(Loc loc, Type t) +private bool checkDefCtor(Loc loc, Type t) { t = t.baseElemOf(); if (t.ty == Tstruct) @@ -519,7 +519,7 @@ private extern (C++) bool checkDefCtor(Loc loc, Type t) * Returns: * true errors happened */ -private extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix) +private bool functionParameters(Loc loc, Scope* sc, TypeFunction tf, Type tthis, Expressions* arguments, FuncDeclaration fd, Type* prettype, Expression* peprefix) { //printf("functionParameters() %s\n", fd ? fd.toChars() : ""); assert(arguments); @@ -1143,7 +1143,7 @@ private extern (C++) bool functionParameters(Loc loc, Scope* sc, TypeFunction tf return (err || olderrors != global.errors); } -private extern (C++) Module loadStdMath() +private Module loadStdMath() { static __gshared Import impStdMath = null; if (!impStdMath)