From 5327b688775d7c40aa5283730e465760d71d7c03 Mon Sep 17 00:00:00 2001 From: k-hara Date: Tue, 15 Apr 2014 10:55:07 +0900 Subject: [PATCH 1/4] fix Issue 12574 - [ICE](statement.c, line 713) with reduce with wrong tuple arity Make `TemplateInstance::semanticTiargs` reentrant against semantic errors. --- src/template.c | 28 ++++++++++++---- src/template.h | 2 +- src/traits.c | 14 +++++--- test/fail_compilation/ice12574.d | 55 ++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 test/fail_compilation/ice12574.d diff --git a/src/template.c b/src/template.c index 1b0d6e1b4a0b..e4a83fdd3b17 100644 --- a/src/template.c +++ b/src/template.c @@ -6362,9 +6362,13 @@ bool TemplateInstance::semanticTiargs(Scope *sc) //printf("+TemplateInstance::semanticTiargs() %s\n", toChars()); if (semantictiargsdone) return true; - semantictiargsdone = 1; - semanticTiargs(loc, sc, tiargs, 0); - return arrayObjectIsError(tiargs) == 0; + if (semanticTiargs(loc, sc, tiargs, 0)) + { + // cache the result iff semantic analysis succeeded entirely + semantictiargsdone = 1; + return true; + } + return false; } /********************************** @@ -6429,12 +6433,13 @@ bool definitelyValueParameter(Expression *e) * 2: don't devolve Parameter to Type */ -void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) +bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) { // Run semantic on each argument, place results in tiargs[] //printf("+TemplateInstance::semanticTiargs()\n"); if (!tiargs) - return; + return true; + bool err = false; for (size_t j = 0; j < tiargs->dim; j++) { RootObject *o = (*tiargs)[j]; @@ -6478,6 +6483,11 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f j--; continue; } + if (ta->ty == Terror) + { + err = true; + continue; + } (*tiargs)[j] = ta->merge2(); } else if (ea) @@ -6534,6 +6544,11 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f j--; continue; } + if (ea->op == TOKerror) + { + err = true; + continue; + } (*tiargs)[j] = ea; if (ea->op == TOKtype) @@ -6642,6 +6657,7 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va); } #endif + return !err; } bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) @@ -7229,7 +7245,7 @@ Identifier *TemplateInstance::genIdent(Objects *args) Identifier *TemplateInstance::getIdent() { - if (!ident && inst) + if (!ident && inst && !errors) ident = genIdent(tiargs); // need an identifier for name mangling purposes. return ident; } diff --git a/src/template.h b/src/template.h index 8ca222f22fb8..4443dede6fe4 100644 --- a/src/template.h +++ b/src/template.h @@ -349,7 +349,7 @@ class TemplateInstance : public ScopeDsymbol // Internal bool findTemplateDeclaration(Scope *sc, WithScopeSymbol **pwithsym); bool updateTemplateDeclaration(Scope *sc, Dsymbol *s); - static void semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags); + static bool semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags); bool semanticTiargs(Scope *sc); bool findBestMatch(Scope *sc, Expressions *fargs); bool needsTypeInference(Scope *sc, int flag = 0); diff --git a/src/traits.c b/src/traits.c index 26e9631c9f68..b5cfba8c8bcc 100644 --- a/src/traits.c +++ b/src/traits.c @@ -208,7 +208,8 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) if (e->ident != Id::compiles && e->ident != Id::isSame && e->ident != Id::identifier && e->ident != Id::getProtection) { - TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1); + if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1)) + return new ErrorExp(); } size_t dim = e->args ? e->args->dim : 0; Declaration *d; @@ -343,7 +344,8 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) * a symbol should not be folded to a constant. * Bit 1 means don't convert Parameter to Type if Parameter has an identifier */ - TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2); + if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2)) + return new ErrorExp(); if (dim != 1) goto Ldimerror; @@ -375,9 +377,12 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) Scope *sc2 = sc->push(); sc2->flags = sc->flags | SCOPEnoaccesscheck; - TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1); + bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1); sc2->pop(); + if (!ok) + return new ErrorExp(); + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) @@ -769,7 +774,8 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) */ if (dim != 2) goto Ldimerror; - TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0); + if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0)) + return new ErrorExp(); RootObject *o1 = (*e->args)[0]; RootObject *o2 = (*e->args)[1]; Dsymbol *s1 = getDsymbol(o1); diff --git a/test/fail_compilation/ice12574.d b/test/fail_compilation/ice12574.d new file mode 100644 index 000000000000..37c28cab83c3 --- /dev/null +++ b/test/fail_compilation/ice12574.d @@ -0,0 +1,55 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12574.d(41): Error: tuple index 2 exceeds length 2 +fail_compilation/ice12574.d(41): Error: tuple index 2 exceeds 2 +fail_compilation/ice12574.d(54): Error: template instance ice12574.reduce!("a", "a").reduce!(Tuple!(int, int, int)) error instantiating +--- +*/ + +struct Tuple(T...) +{ + alias Types = T; + T field; + alias field this; +} +Tuple!A tuple(A...)(A args) { return typeof(return)(args); } + +template binaryFun(alias fun) +{ + static if (is(typeof(fun) : string)) + { + auto binaryFun(ElementType1, ElementType2)(auto ref ElementType1 __a, auto ref ElementType2 __b) + { + mixin("alias "~"a"~" = __a ;"); + mixin("alias "~"b"~" = __b ;"); + return mixin(fun); + } + } + else + { + alias binaryFun = fun; + } +} + +template reduce(fun...) +{ + auto reduce(Seed)(Seed result) + { + foreach (i, Unused; Seed.Types) + { + result[i] = binaryFun!(fun[i])(1, 1); // here + } + return result; + } +} + +int foo(int value) +{ + return value; +} + +void main() +{ + reduce!("a", "a")(tuple(1, 1, 1)); +} From 42d3a795438c2820aeb59945cd2f132499eda6c5 Mon Sep 17 00:00:00 2001 From: k-hara Date: Tue, 15 Apr 2014 11:29:08 +0900 Subject: [PATCH 2/4] [Refactoring] Make long member function names short --- src/expression.c | 8 ++++---- src/template.c | 14 +++++++------- src/template.h | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/expression.c b/src/expression.c index d873a4760e39..c9798c70b399 100644 --- a/src/expression.c +++ b/src/expression.c @@ -734,7 +734,7 @@ Expression *searchUFCS(Scope *sc, UnaExp *ue, Identifier *ident) DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ue; TemplateInstance *ti = new TemplateInstance(loc, s->ident); ti->tiargs = dti->ti->tiargs; // for better diagnostic message - if (!ti->updateTemplateDeclaration(sc, s)) + if (!ti->updateTempDecl(sc, s)) return new ErrorExp(); return new ScopeExp(loc, ti); } @@ -4555,7 +4555,7 @@ Expression *ScopeExp::semantic(Scope *sc) if (ti) { WithScopeSymbol *withsym; - if (!ti->findTemplateDeclaration(sc, &withsym) || + if (!ti->findTempDecl(sc, &withsym) || !ti->semanticTiargs(sc)) { ti->inst = ti; @@ -7545,7 +7545,7 @@ bool DotTemplateInstanceExp::findTempDecl(Scope *sc) case TOKvar: s = ((VarExp *)e)->var; break; default: return false; } - return ti->updateTemplateDeclaration(sc, s); + return ti->updateTempDecl(sc, s); } Expression *DotTemplateInstanceExp::semantic(Scope *sc) @@ -7930,7 +7930,7 @@ Expression *CallExp::semantic(Scope *sc) * If not, go with partial explicit specialization. */ WithScopeSymbol *withsym; - if (!ti->findTemplateDeclaration(sc, &withsym) || + if (!ti->findTempDecl(sc, &withsym) || !ti->semanticTiargs(sc)) { ti->inst = ti; diff --git a/src/template.c b/src/template.c index e4a83fdd3b17..da0407eef2fe 100644 --- a/src/template.c +++ b/src/template.c @@ -5714,7 +5714,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) * then run semantic on each argument (place results in tiargs[]), * last find most specialized template from overload list/set. */ - if (!findTemplateDeclaration(sc, NULL) || + if (!findTempDecl(sc, NULL) || !semanticTiargs(sc) || !findBestMatch(sc, fargs)) { @@ -6164,7 +6164,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) * Find template declaration corresponding to template instance. */ -bool TemplateInstance::findTemplateDeclaration(Scope *sc, WithScopeSymbol **pwithsym) +bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym) { if (pwithsym) *pwithsym = NULL; @@ -6172,7 +6172,7 @@ bool TemplateInstance::findTemplateDeclaration(Scope *sc, WithScopeSymbol **pwit if (havetempdecl) return true; - //printf("TemplateInstance::findTemplateDeclaration() %s\n", toChars()); + //printf("TemplateInstance::findTempDecl() %s\n", toChars()); if (!tempdecl) { /* Given: @@ -6221,7 +6221,7 @@ bool TemplateInstance::findTemplateDeclaration(Scope *sc, WithScopeSymbol **pwit } } - if (!updateTemplateDeclaration(sc, s)) + if (!updateTempDecl(sc, s)) { return false; } @@ -6269,7 +6269,7 @@ bool TemplateInstance::findTemplateDeclaration(Scope *sc, WithScopeSymbol **pwit * Confirm s is a valid template, then store it. */ -bool TemplateInstance::updateTemplateDeclaration(Scope *sc, Dsymbol *s) +bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s) { if (s) { @@ -7650,7 +7650,7 @@ Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *s) return tm; } -bool TemplateMixin::findTemplateDeclaration(Scope *sc) +bool TemplateMixin::findTempDecl(Scope *sc) { // Follow qualifications to find the TemplateDeclaration if (!tempdecl) @@ -7770,7 +7770,7 @@ void TemplateMixin::semantic(Scope *sc) /* Run semantic on each argument, place results in tiargs[], * then find best match template with tiargs */ - if (!findTemplateDeclaration(sc) || + if (!findTempDecl(sc) || !semanticTiargs(sc) || !findBestMatch(sc, NULL)) { diff --git a/src/template.h b/src/template.h index 4443dede6fe4..4a0d1a91bb83 100644 --- a/src/template.h +++ b/src/template.h @@ -347,8 +347,8 @@ class TemplateInstance : public ScopeDsymbol void toObjFile(bool multiobj); // compile to .obj file // Internal - bool findTemplateDeclaration(Scope *sc, WithScopeSymbol **pwithsym); - bool updateTemplateDeclaration(Scope *sc, Dsymbol *s); + bool findTempDecl(Scope *sc, WithScopeSymbol **pwithsym); + bool updateTempDecl(Scope *sc, Dsymbol *s); static bool semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags); bool semanticTiargs(Scope *sc); bool findBestMatch(Scope *sc, Expressions *fargs); @@ -385,7 +385,7 @@ class TemplateMixin : public TemplateInstance void toObjFile(bool multiobj); // compile to .obj file - bool findTemplateDeclaration(Scope *sc); + bool findTempDecl(Scope *sc); TemplateMixin *isTemplateMixin() { return this; } void accept(Visitor *v) { v->visit(this); } From 0b002fc10eb0d09bfb4a30607a3b31c6ed4f6f45 Mon Sep 17 00:00:00 2001 From: k-hara Date: Tue, 15 Apr 2014 16:02:53 +0900 Subject: [PATCH 3/4] [Refactoring] Add detailed comment --- src/template.c | 144 ++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 56 deletions(-) diff --git a/src/template.c b/src/template.c index da0407eef2fe..59c2b0d4bc24 100644 --- a/src/template.c +++ b/src/template.c @@ -201,6 +201,62 @@ Expression *getValue(Dsymbol *&s) return e; } +/********************************** + * Return true if e could be valid only as a template value parameter. + * Return false if it might be an alias or tuple. + * (Note that even in this case, it could still turn out to be a value). + */ +bool definitelyValueParameter(Expression *e) +{ + // None of these can be value parameters + if (e->op == TOKtuple || e->op == TOKimport || + e->op == TOKtype || e->op == TOKdottype || + e->op == TOKtemplate || e->op == TOKdottd || + e->op == TOKfunction || e->op == TOKerror || + e->op == TOKthis || e->op == TOKsuper) + return false; + + if (e->op != TOKdotvar) + return true; + + /* Template instantiations involving a DotVar expression are difficult. + * In most cases, they should be treated as a value parameter, and interpreted. + * But they might also just be a fully qualified name, which should be treated + * as an alias. + */ + + // x.y.f cannot be a value + FuncDeclaration *f = ((DotVarExp *)e)->var->isFuncDeclaration(); + if (f) + return false; + + while (e->op == TOKdotvar) + { + e = ((DotVarExp *)e)->e1; + } + // this.x.y and super.x.y couldn't possibly be valid values. + if (e->op == TOKthis || e->op == TOKsuper) + return false; + + // e.type.x could be an alias + if (e->op == TOKdottype) + return false; + + // var.x.y is the only other possible form of alias + if (e->op != TOKvar) + return true; + + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + + // func.x.y is not an alias + if (!v) + return true; + + // TODO: Should we force CTFE if it is a global constant? + + return false; +} + /****************************** * If o1 matches o2, return 1. * Else, return 0. @@ -6162,6 +6218,13 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) /********************************************** * Find template declaration corresponding to template instance. + * + * Returns: + * false if finding fails. + * Note: + * This function is reentrant against error occurrence. If returns false, + * any members of this object won't be modified, and repetition call will + * reproduce same error. */ bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym) @@ -6267,6 +6330,14 @@ bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym) /********************************************** * Confirm s is a valid template, then store it. + * Input: + * sc + * s candidate symbol of template. It may be: + * TemplateDeclaration + * FuncDeclaration with findTemplateDeclRoot() != NULL + * OverloadSet which contains candidates + * Returns: + * true if updating succeeds. */ bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s) @@ -6357,6 +6428,17 @@ bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s) return (tempdecl != NULL); } +/********************************** + * Run semantic on the elements of tiargs. + * Input: + * sc + * Returns: + * false if one or more arguments have errors. + * Note: + * This function is reentrant against error occurrence. If returns false, + * all elements of tiargs won't be modified. + */ + bool TemplateInstance::semanticTiargs(Scope *sc) { //printf("+TemplateInstance::semanticTiargs() %s\n", toChars()); @@ -6372,65 +6454,15 @@ bool TemplateInstance::semanticTiargs(Scope *sc) } /********************************** - * Return true if e could be valid only as a template value parameter. - * Return false if it might be an alias or tuple. - * (Note that even in this case, it could still turn out to be a value). - */ -bool definitelyValueParameter(Expression *e) -{ - // None of these can be value parameters - if (e->op == TOKtuple || e->op == TOKimport || - e->op == TOKtype || e->op == TOKdottype || - e->op == TOKtemplate || e->op == TOKdottd || - e->op == TOKfunction || e->op == TOKerror || - e->op == TOKthis || e->op == TOKsuper) - return false; - - if (e->op != TOKdotvar) - return true; - - /* Template instantiations involving a DotVar expression are difficult. - * In most cases, they should be treated as a value parameter, and interpreted. - * But they might also just be a fully qualified name, which should be treated - * as an alias. - */ - - // x.y.f cannot be a value - FuncDeclaration *f = ((DotVarExp *)e)->var->isFuncDeclaration(); - if (f) - return false; - - while (e->op == TOKdotvar) - { - e = ((DotVarExp *)e)->e1; - } - // this.x.y and super.x.y couldn't possibly be valid values. - if (e->op == TOKthis || e->op == TOKsuper) - return false; - - // e.type.x could be an alias - if (e->op == TOKdottype) - return false; - - // var.x.y is the only other possible form of alias - if (e->op != TOKvar) - return true; - - VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); - - // func.x.y is not an alias - if (!v) - return true; - - // TODO: Should we force CTFE if it is a global constant? - - return false; -} - -/********************************** + * Run semantic of tiargs as arguments of template. * Input: + * loc + * sc + * tiargs array of template arguments * flags 1: replace const variables with their initializers * 2: don't devolve Parameter to Type + * Returns: + * false if one or more arguments have errors. */ bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) From de1a6e570e2b7a135d40ce0bebc312be90e2101a Mon Sep 17 00:00:00 2001 From: k-hara Date: Tue, 15 Apr 2014 17:07:18 +0900 Subject: [PATCH 4/4] fix Issue 12581 - [ICE](statement.c, line 713) with invalid assignment + alias this Make AssignExp::semantic reentrant for `op_overload` call. ----- If an expression supports operator overloading, its `semantic` function should not store error expressions in itself until the point of `op_olverload` call (from [a] to [b]). Example: Expression *XXXExp::semantic(Scope sc) { // [a] ... e1 = e1->semantic(sc); // Bad: if semantic analysis returns ErrorExp, // it is stored in this->e1 immediately. if (e1->op == TOKerror) return e1; // [b] Expression *e = op_overload(sc); if (e) return e; ... } If `XXXExp::semantic` is called from the `trySemantic` call for `alias this` analysis (from `op_overload` function in `opover.c`), the stored ErrorExp would accidentally escape from the gagged period. --- src/expression.c | 424 +++++++++++++++++-------------- test/fail_compilation/ice12581.d | 22 ++ 2 files changed, 256 insertions(+), 190 deletions(-) create mode 100644 test/fail_compilation/ice12581.d diff --git a/src/expression.c b/src/expression.c index c9798c70b399..59696afec910 100644 --- a/src/expression.c +++ b/src/expression.c @@ -9079,15 +9079,16 @@ UAddExp::UAddExp(Loc loc, Expression *e) } Expression *UAddExp::semantic(Scope *sc) -{ Expression *e; - +{ #if LOGSEMANTIC printf("UAddExp::semantic('%s')\n", toChars()); #endif assert(!type); - e = op_overload(sc); + + Expression *e = op_overload(sc); if (e) return e; + e1->checkNoBool(); e1->checkArithmetic(); return e1; @@ -9339,7 +9340,10 @@ Expression *CastExp::semantic(Scope *sc) if (Expression *ex = unaSemantic(sc)) return ex; - e1 = resolveProperties(sc, e1); + Expression *e1x = resolveProperties(sc, e1); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; if (e1->type) // if not a tuple { @@ -9358,8 +9362,6 @@ Expression *CastExp::semantic(Scope *sc) error("cannot cast %s to tuple type %s", e1->toChars(), to->toChars()); return new ErrorExp(); } - if (e1->type->ty == Terror) - return new ErrorExp(); // cast(void) is used to mark e1 as unused, so it is safe if (to->ty == Tvoid) @@ -9369,9 +9371,7 @@ Expression *CastExp::semantic(Scope *sc) { Expression *e = op_overload(sc); if (e) - { return e->implicitCastTo(sc, to); - } } if (e1->op == TOKtemplate) @@ -10022,9 +10022,10 @@ Expression *ArrayExp::semantic(Scope *sc) #endif if (Expression *ex = unaSemantic(sc)) return ex; - e1 = resolveProperties(sc, e1); - if (e1->op == TOKerror) - return e1; + Expression *e1x = resolveProperties(sc, e1); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; Type *t1 = e1->type->toBasetype(); if (t1->ty != Tclass && t1->ty != Tstruct) @@ -10419,7 +10420,10 @@ Expression *PostExp::semantic(Scope *sc) if (Expression *ex = binSemantic(sc)) return ex; - e1 = resolveProperties(sc, e1); + Expression *e1x = resolveProperties(sc, e1); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; Expression *e = op_overload(sc); if (e) @@ -10587,7 +10591,6 @@ Expression *AssignExp::semantic(Scope *sc) // No opIndexAssign found yet, but there might be an alias this to try. if (ad->aliasthis && t1 != ae->att1) { - Expression *e2x = e2; ArrayExp *aex = (ArrayExp *)ae->copy(); if (!aex->att1 && t1->checkAliasThisRec()) aex->att1 = t1; @@ -10597,7 +10600,6 @@ Expression *AssignExp::semantic(Scope *sc) if (ex) return ex; this->e1 = ae; // restore - this->e2 = e2x; // restore } Lfallback: @@ -10658,7 +10660,6 @@ Expression *AssignExp::semantic(Scope *sc) // No opSliceAssign found yet, but there might be an alias this to try. if (ad->aliasthis && t1 != ae->att1) { - Expression *e2x = e2; SliceExp *aex = (SliceExp *)ae->copy(); if (!aex->att1 && t1->checkAliasThisRec()) aex->att1 = t1; @@ -10668,122 +10669,142 @@ Expression *AssignExp::semantic(Scope *sc) if (ex) return ex; this->e1 = ae; // restore - this->e2 = e2x; // restore } } } - /* With UFCS, e.f = value - * Could mean: - * .f(e, value) - * or: - * .f(e) = value + /* Run this->e1 semantic. */ - if (e1->op == TOKdotti) - { - DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1; - Expression *e = dti->semanticY(sc, 1); - if (!e) - return resolveUFCSProperties(sc, e1, e2); - e1 = e; - } - else if (e1->op == TOKdot) { - DotIdExp *die = (DotIdExp *)e1; - Expression *e = die->semanticY(sc, 1); - if (e && isDotOpDispatch(e)) + Expression *e1x = e1; + + /* With UFCS, e.f = value + * Could mean: + * .f(e, value) + * or: + * .f(e) = value + */ + if (e1x->op == TOKdotti) { - unsigned errors = global.startGagging(); - e = resolvePropertiesX(sc, e, e2); - if (global.endGagging(errors)) - e = NULL; /* fall down to UFCS */ - else - return e; + DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1x; + Expression *e = dti->semanticY(sc, 1); + if (!e) + return resolveUFCSProperties(sc, e1x, e2); + e1x = e; } - if (!e) - return resolveUFCSProperties(sc, e1, e2); - e1 = e; - } - else - e1 = e1->semantic(sc); - if (e1->op == TOKerror) - return new ErrorExp(); + else if (e1x->op == TOKdot) + { + DotIdExp *die = (DotIdExp *)e1x; + Expression *e = die->semanticY(sc, 1); + if (e && isDotOpDispatch(e)) + { + unsigned errors = global.startGagging(); + e = resolvePropertiesX(sc, e, e2); + if (global.endGagging(errors)) + e = NULL; /* fall down to UFCS */ + else + return e; + } + if (!e) + return resolveUFCSProperties(sc, e1x, e2); + e1x = e; + } + else + e1x = e1x->semantic(sc); - /* We have f = value. - * Could mean: - * f(value) - * or: - * f() = value - */ - if (Expression *e = resolvePropertiesX(sc, e1, e2)) - return e; + /* We have f = value. + * Could mean: + * f(value) + * or: + * f() = value + */ + if (Expression *e = resolvePropertiesX(sc, e1x, e2)) + return e; - e1 = checkRightThis(sc, e1); + e1x = checkRightThis(sc, e1x); - assert(e1->type); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; + assert(e1->type); + } Type *t1 = e1->type->toBasetype(); - e2 = inferType(e2, t1); + /* Run this->e2 semantic. + * Different from other binary expressions, the analysis of e2 + * depends on the result of e1 in assignments. + */ + { + Expression *e2x = inferType(e2, t1); - e2 = e2->semantic(sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - if (!e2->rvalue()) - return new ErrorExp(); + e2x = e2x->semantic(sc); + e2x = resolveProperties(sc, e2x); + + if (e2x->op == TOKerror) + return e2x; + if (!e2x->rvalue()) + return new ErrorExp(); + e2 = e2x; + } /* Rewrite tuple assignment as a tuple of assignments. */ -Ltupleassign: - if (e1->op == TOKtuple && e2->op == TOKtuple) { - TupleExp *tup1 = (TupleExp *)e1; - TupleExp *tup2 = (TupleExp *)e2; - size_t dim = tup1->exps->dim; - Expression *e = NULL; - if (dim != tup2->exps->dim) - { - error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim); - return new ErrorExp(); - } - if (dim == 0) - { - e = new IntegerExp(loc, 0, Type::tint32); - e = new CastExp(loc, e, Type::tvoid); // avoid "has no effect" error - e = combine(combine(tup1->e0, tup2->e0), e); - } - else + Expression *e2x = e2; + + Ltupleassign: + if (e1->op == TOKtuple && e2x->op == TOKtuple) { - Expressions *exps = new Expressions; - exps->setDim(dim); - for (size_t i = 0; i < dim; i++) + TupleExp *tup1 = (TupleExp *)e1; + TupleExp *tup2 = (TupleExp *)e2x; + size_t dim = tup1->exps->dim; + Expression *e = NULL; + if (dim != tup2->exps->dim) { - Expression *ex1 = (*tup1->exps)[i]; - Expression *ex2 = (*tup2->exps)[i]; - (*exps)[i] = new AssignExp(loc, ex1, ex2); + error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim); + return new ErrorExp(); + } + if (dim == 0) + { + e = new IntegerExp(loc, 0, Type::tint32); + e = new CastExp(loc, e, Type::tvoid); // avoid "has no effect" error + e = combine(combine(tup1->e0, tup2->e0), e); } - e = new TupleExp(loc, combine(tup1->e0, tup2->e0), exps); + else + { + Expressions *exps = new Expressions; + exps->setDim(dim); + for (size_t i = 0; i < dim; i++) + { + Expression *ex1 = (*tup1->exps)[i]; + Expression *ex2 = (*tup2->exps)[i]; + (*exps)[i] = new AssignExp(loc, ex1, ex2); + } + e = new TupleExp(loc, combine(tup1->e0, tup2->e0), exps); + } + return e->semantic(sc); } - assert(e); - return e->semantic(sc); - } - if (e1->op == TOKtuple) - { - if (TupleDeclaration *td = isAliasThisTuple(e2)) + /* Look for form: e1 = e2->aliasthis. + */ + if (e1->op == TOKtuple) { + TupleDeclaration *td = isAliasThisTuple(e2x); + if (!td) + goto Lnomatch; + assert(e1->type->ty == Ttuple); TypeTuple *tt = (TypeTuple *)e1->type; Identifier *id = Lexer::uniqueId("__tup"); - ExpInitializer *ei = new ExpInitializer(e2->loc, e2); - VarDeclaration *v = new VarDeclaration(e2->loc, NULL, id, ei); + ExpInitializer *ei = new ExpInitializer(e2x->loc, e2x); + VarDeclaration *v = new VarDeclaration(e2x->loc, NULL, id, ei); v->storage_class |= STCtemp | STCctfe; - if (e2->isLvalue()) + if (e2x->isLvalue()) v->storage_class = STCref | STCforeach; - Expression *e0 = new DeclarationExp(e2->loc, v); - Expression *ev = new VarExp(e2->loc, v); - ev->type = e2->type; + Expression *e0 = new DeclarationExp(e2x->loc, v); + Expression *ev = new VarExp(e2x->loc, v); + ev->type = e2x->type; Expressions *iexps = new Expressions(); iexps->push(ev); @@ -10807,15 +10828,20 @@ Expression *AssignExp::semantic(Scope *sc) goto Lnomatch; } } - e2 = new TupleExp(e2->loc, e0, iexps); - e2 = e2->semantic(sc); + e2x = new TupleExp(e2x->loc, e0, iexps); + e2x = e2x->semantic(sc); + if (e2x->op == TOKerror) + return e2x; + // Do not need to overwrite this->e2 goto Ltupleassign; - - Lnomatch: - ; } + Lnomatch: + ; } + /* Inside constructor, if this is the first assignment of object field, + * rewrite this to initializing the field. + */ if (op == TOKassign && e1->checkModifiable(sc) == 2) { //printf("[%s] change to init - %s\n", loc.toChars(), toChars()); @@ -10833,19 +10859,22 @@ Expression *AssignExp::semantic(Scope *sc) } else if (t1->ty == Tstruct) { + Expression *e1x = e1; + Expression *e2x = e2; StructDeclaration *sd = ((TypeStruct *)t1)->sym; + if (op == TOKconstruct) { - Type *t2 = e2->type->toBasetype(); + Type *t2 = e2x->type->toBasetype(); if (t2->ty == Tstruct && sd == ((TypeStruct *)t2)->sym) { CallExp *ce; DotVarExp *dve; if (sd->ctor && - e2->op == TOKcall && - (ce = (CallExp *)e2, ce->e1->op == TOKdotvar) && + e2x->op == TOKcall && + (ce = (CallExp *)e2x, ce->e1->op == TOKdotvar) && (dve = (DotVarExp *)ce->e1, dve->var->isCtorDeclaration()) && - e2->type->implicitConvTo(t1)) + e2x->type->implicitConvTo(t1)) { /* Look for form of constructor call which is: * __ctmp.ctor(arguments...) @@ -10863,14 +10892,14 @@ Expression *AssignExp::semantic(Scope *sc) else ae->e2 = t1->defaultInit(loc); // Keep ae->op == TOKconstruct - ae->type = e1->type; + ae->type = e1x->type; /* Replace __ctmp being constructed with e1. * We need to copy constructor call expression, * because it may be used in other place. */ DotVarExp *dvx = (DotVarExp *)dve->copy(); - dvx->e1 = this->e1; + dvx->e1 = e1x; CallExp *cx = (CallExp *)ce->copy(); cx->e1 = dvx; @@ -10882,28 +10911,31 @@ Expression *AssignExp::semantic(Scope *sc) { /* We have a copy constructor for this */ - if (e2->op == TOKquestion) + if (e2x->op == TOKquestion) { /* Rewrite as: * a ? e1 = b : e1 = c; */ - CondExp *econd = (CondExp *)e2; - Expression *ea1 = new ConstructExp(econd->e1->loc, e1, econd->e1); - Expression *ea2 = new ConstructExp(econd->e1->loc, e1, econd->e2); + CondExp *econd = (CondExp *)e2x; + Expression *ea1 = new ConstructExp(econd->e1->loc, e1x, econd->e1); + Expression *ea2 = new ConstructExp(econd->e1->loc, e1x, econd->e2); Expression *e = new CondExp(loc, econd->econd, ea1, ea2); return e->semantic(sc); } - if (e2->isLvalue()) + if (e2x->isLvalue()) { /* Rewrite as: * e1.cpctor(e2); */ - if (!e2->type->implicitConvTo(e1->type)) - error("conversion error from %s to %s", e2->type->toChars(), e1->type->toChars()); + if (!e2x->type->implicitConvTo(e1x->type)) + { + error("conversion error from %s to %s", e2x->type->toChars(), e1x->type->toChars()); + return new ErrorExp(); + } - Expression *e = new DotVarExp(loc, e1, sd->cpctor, 0); - e = new CallExp(loc, e, e2); + Expression *e = new DotVarExp(loc, e1x, sd->cpctor, 0); + e = new CallExp(loc, e, e2x); return e->semantic(sc); } else @@ -10911,11 +10943,11 @@ Expression *AssignExp::semantic(Scope *sc) /* The struct value returned from the function is transferred * so should not call the destructor on it. */ - e2 = valueNoDtor(e2); + e2x = valueNoDtor(e2x); } } } - else if (!e2->implicitConvTo(t1)) + else if (!e2x->implicitConvTo(t1)) { if (sd->ctor) { @@ -10923,41 +10955,41 @@ Expression *AssignExp::semantic(Scope *sc) * Rewrite as: * e1 = init, e1.ctor(e2) */ - Expression *ex; - ex = new AssignExp(loc, e1, e1->type->defaultInit(loc)); - ex->op = TOKblit; - ex->type = e1->type; + Expression *einit; + einit = new AssignExp(loc, e1x, e1x->type->defaultInit(loc)); + einit->op = TOKblit; + einit->type = e1x->type; Expression *e; - e = new DotIdExp(loc, e1, Id::ctor); - e = new CallExp(loc, e, e2); - e = new CommaExp(loc, ex, e); + e = new DotIdExp(loc, e1x, Id::ctor); + e = new CallExp(loc, e, e2x); + e = new CommaExp(loc, einit, e); e = e->semantic(sc); return e; } - else if (search_function(sd, Id::call)) + if (search_function(sd, Id::call)) { /* Look for static opCall * (See bugzilla 2702 for more discussion) * Rewrite as: * e1 = typeof(e1).opCall(arguments) */ - Expression *e = typeDotIdExp(e2->loc, e1->type, Id::call); - e2 = new CallExp(loc, e, e2); - - e2 = e2->semantic(sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - if (!e2->rvalue()) + e2x = typeDotIdExp(e2x->loc, e1x->type, Id::call); + e2x = new CallExp(loc, e2x, this->e2); + + e2x = e2x->semantic(sc); + e2x = resolveProperties(sc, e2x); + if (e2x->op == TOKerror) + return e2x; + if (!e2x->rvalue()) return new ErrorExp(); } } } else if (op == TOKassign) { - if (e1->op == TOKindex && - ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + if (e1x->op == TOKindex && + ((IndexExp *)e1x)->e1->type->toBasetype()->ty == Taarray) { /* * Rewrite: @@ -10970,13 +11002,13 @@ Expression *AssignExp::semantic(Scope *sc) * ? __aatmp[__aakey].opAssign(__aaval) * : ConstructExp(__aatmp[__aakey], __aaval)); */ - IndexExp *ie = (IndexExp *)e1; - Type *t2 = e2->type->toBasetype(); + IndexExp *ie = (IndexExp *)e1x; + Type *t2 = e2x->type->toBasetype(); Expression *e0 = NULL; Expression *ea = ie->e1; Expression *ek = ie->e2; - Expression *ev = e2; + Expression *ev = e2x; if (hasSideEffect(ea)) { VarDeclaration *v = new VarDeclaration(loc, ie->e1->type, @@ -11001,8 +11033,8 @@ Expression *AssignExp::semantic(Scope *sc) } if (hasSideEffect(ev)) { - VarDeclaration *v = new VarDeclaration(loc, e2->type, - Lexer::uniqueId("__aaval"), new ExpInitializer(loc, e2)); + VarDeclaration *v = new VarDeclaration(loc, e2x->type, + Lexer::uniqueId("__aaval"), new ExpInitializer(loc, e2x)); v->storage_class |= STCtemp | STCctfe; if (ev->isLvalue()) v->storage_class |= STCforeach | STCref; @@ -11018,7 +11050,6 @@ Expression *AssignExp::semantic(Scope *sc) ae->e1 = ae->e1->semantic(sc); ae->e1 = ae->e1->optimize(WANTvalue); ae->e2 = ev; - //Expression *e = new CallExp(loc, new DotIdExp(loc, ex, Id::assign), ev); Expression *e = ae->op_overload(sc); if (e) { @@ -11063,6 +11094,9 @@ Expression *AssignExp::semantic(Scope *sc) } else assert(op == TOKblit); + + e1 = e1x; + e2 = e2x; } else if (t1->ty == Tclass) { @@ -11076,35 +11110,36 @@ Expression *AssignExp::semantic(Scope *sc) } else if (t1->ty == Tsarray) { - Type *t2 = e2->type->toBasetype(); + Expression *e1x = e1; + Expression *e2x = e2; + Type *t2 = e2x->type->toBasetype(); - if (e1->op == TOKindex && - ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + if (e1x->op == TOKindex && + ((IndexExp *)e1x)->e1->type->toBasetype()->ty == Taarray) { // Assignment to an AA of fixed-length arrays. // Convert T[n][U] = T[] into T[n][U] = T[n] - e2 = e2->implicitCastTo(sc, e1->type); - if (e2->op == TOKerror) - return e2; + e2x = e2x->implicitCastTo(sc, e1x->type); + if (e2x->op == TOKerror) + return e2x; } else if (op == TOKconstruct) { - Expression *e2x = e2; if (e2x->op == TOKslice) { - SliceExp *se = (SliceExp *)e2; - if (se->lwr == NULL && se->e1->implicitConvTo(e1->type)) + SliceExp *se = (SliceExp *)e2x; + if (se->lwr == NULL && se->e1->implicitConvTo(e1x->type)) { e2x = se->e1; } } if (e2x->op == TOKcall && !e2x->isLvalue() && - e2x->implicitConvTo(e1->type)) + e2x->implicitConvTo(e1x->type)) { // Keep the expression form for NRVO - e2 = e2x->implicitCastTo(sc, e1->type); - if (e2->op == TOKerror) - return e2; + e2x = e2x->implicitCastTo(sc, e1x->type); + if (e2x->op == TOKerror) + return e2x; } else { @@ -11114,12 +11149,12 @@ Expression *AssignExp::semantic(Scope *sc) * sa = [...]; as: sa[] = [...]; */ // Convert e2 to e2[], if t2 is impllicitly convertible to t1. - if (e2->op != TOKarrayliteral && t2->ty == Tsarray && t2->implicitConvTo(t1)) + if (e2x->op != TOKarrayliteral && t2->ty == Tsarray && t2->implicitConvTo(t1)) { - e2 = new SliceExp(e2->loc, e2, NULL, NULL); - e2 = e2->semantic(sc); + e2x = new SliceExp(e2x->loc, e2x, NULL, NULL); + e2x = e2x->semantic(sc); } - else if (!e2->implicitConvTo(e1->type)) + else if (!e2x->implicitConvTo(e1x->type)) { // If multidimensional static array, treat as one large array dinteger_t dim = ((TypeSArray *)t1)->dim->toInteger(); @@ -11130,14 +11165,13 @@ Expression *AssignExp::semantic(Scope *sc) if (t->ty != Tsarray) break; dim *= ((TypeSArray *)t)->dim->toInteger(); - e1->type = t->nextOf()->sarrayOf(dim); + e1x->type = t->nextOf()->sarrayOf(dim); } } // Convert e1 to e1[] - e1 = new SliceExp(e1->loc, e1, NULL, NULL); - e1 = e1->semantic(sc); - t1 = e1->type->toBasetype(); + e1x = new SliceExp(e1x->loc, e1x, NULL, NULL); + e1x = e1x->semantic(sc); } } else if (op == TOKassign) @@ -11149,19 +11183,19 @@ Expression *AssignExp::semantic(Scope *sc) */ // Convert e2 to e2[], unless e2-> e1[0] - if (e2->op != TOKarrayliteral && t2->ty == Tsarray && !t2->implicitConvTo(t1->nextOf())) + if (e2x->op != TOKarrayliteral && t2->ty == Tsarray && !t2->implicitConvTo(t1->nextOf())) { - e2 = new SliceExp(e2->loc, e2, NULL, NULL); - e2 = e2->semantic(sc); + e2x = new SliceExp(e2x->loc, e2x, NULL, NULL); + e2x = e2x->semantic(sc); } else if (0 && global.params.warnings && !global.gag && op == TOKassign && - e2->op != TOKarrayliteral && e2->op != TOKstring && - !e2->implicitConvTo(t1)) + e2x->op != TOKarrayliteral && e2x->op != TOKstring && + !e2x->implicitConvTo(t1)) { // Disallow sa = da (Converted to sa[] = da[]) // Disallow sa = e (Converted to sa[] = e) - const char* e1str = e1->toChars(); - const char* e2str = e2->toChars(); - if (e2->op == TOKslice || e2->implicitConvTo(t1->nextOf())) + const char* e1str = e1x->toChars(); + const char* e2str = e2x->toChars(); + if (e2x->op == TOKslice || e2x->implicitConvTo(t1->nextOf())) warning("explicit element-wise assignment (%s)[] = %s is better than %s = %s", e1str, e2str, e1str, e2str); else @@ -11171,21 +11205,20 @@ Expression *AssignExp::semantic(Scope *sc) // Convert e2 to e2[] to avoid duplicated error message. if (t2->ty == Tarray) { - Expression *e = new SliceExp(e2->loc, e2, NULL, NULL); - e2 = e->semantic(sc); + e2x = new SliceExp(e2x->loc, e2x, NULL, NULL); + e2x = e2x->semantic(sc); } } // Convert e1 to e1[] - e1 = new SliceExp(e1->loc, e1, NULL, NULL); - e1 = e1->semantic(sc); - t1 = e1->type->toBasetype(); + e1x = new SliceExp(e1x->loc, e1x, NULL, NULL); + e1x = e1x->semantic(sc); } else { assert(op == TOKblit); - if (!e2->implicitConvTo(e1->type)) + if (!e2x->implicitConvTo(e1x->type)) { /* Internal handling for the default initialization * of multi-dimensional static array: @@ -11200,13 +11233,20 @@ Expression *AssignExp::semantic(Scope *sc) if (t->ty != Tsarray) break; dim *= ((TypeSArray *)t)->dim->toInteger(); - e1->type = t->nextOf()->sarrayOf(dim); + e1x->type = t->nextOf()->sarrayOf(dim); } } - e1 = new SliceExp(loc, e1, NULL, NULL); - e1 = e1->semantic(sc); - t1 = e1->type->toBasetype(); + e1x = new SliceExp(loc, e1x, NULL, NULL); + e1x = e1x->semantic(sc); } + if (e1x->op == TOKerror) + return e1x; + if (e2x->op == TOKerror) + return e2x; + + e1 = e1x; + e2 = e2x; + t1 = e1x->type->toBasetype(); } /* Check the mutability of e1. @@ -11246,6 +11286,8 @@ Expression *AssignExp::semantic(Scope *sc) if (e1->op == TOKerror) return e1; + /* Tweak e2 based on the type of e1. + */ Type *t2 = e2->type->toBasetype(); // If it is a array, get the element type. Note that it may be @@ -11278,18 +11320,18 @@ Expression *AssignExp::semantic(Scope *sc) } else assert(op != TOKassign); - //error("cannot assign to static array %s", e1->toChars()); } else if (e1->op == TOKslice && (t2->ty == Tarray || t2->ty == Tsarray) && t2->nextOf()->implicitConvTo(t1->nextOf())) { // Check element-wise assignment. + SliceExp *se1 = (SliceExp *)e1; + Type *tx1 = se1->e1->type->toBasetype(); + /* If assigned elements number is known at compile time, * check the mismatch. */ - SliceExp *se1 = (SliceExp *)e1; - Type *tx1 = se1->e1->type->toBasetype(); if (se1->lwr == NULL && tx1->ty == Tsarray) { Type *tx2 = t2; @@ -11511,6 +11553,9 @@ CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2) Expression *CatAssignExp::semantic(Scope *sc) { + if (type) + return this; + //printf("CatAssignExp::semantic() %s\n", toChars()); Expression *e = op_overload(sc); if (e) @@ -11727,7 +11772,6 @@ Expression *PowAssignExp::semantic(Scope *sc) return incompatibleTypes(); } - /************************* AddExp *****************************/ AddExp::AddExp(Loc loc, Expression *e1, Expression *e2) diff --git a/test/fail_compilation/ice12581.d b/test/fail_compilation/ice12581.d new file mode 100644 index 000000000000..976e10385b5c --- /dev/null +++ b/test/fail_compilation/ice12581.d @@ -0,0 +1,22 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12581.d(21): Error: undefined identifier undef +--- +*/ + +struct S +{ + int[3] a; + alias a this; +} +struct T +{ + S s; + alias s this; +} +void main() +{ + T x; + x[] = (undef = 1); +}