Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Issue 6365 - Multiple var declaration (AutoTupleDeclaration) #341

Closed
wants to merge 23 commits into from
@9rnsr
Collaborator

http://d.puremagic.com/issues/show_bug.cgi?id=6365

Based on #321, declare multiple variables at once, and initialize them in the corresponding tuple fields.

Syntax:

TupleDeclaration:
    StorageClasses ( TupleTypeList ) = Initializer ;
    ( TupleTypeList ) = Initializer ;

TupleTypeList:
    TupleType
    TupleType , TupleTypeList

TupleType:
    StorageClasses BasicType Declarator
    BasicType Declarator
    StorageClasses Identifier
    Identifier

TupleTypeList is similar to ForeachTypeList.

If you want to write left parenthesis at beginning, you cannot omit the type or storage class of first variable.

(x, y) = getCoordinate();  // NG, this is not multiple var declaration
(auto x, y) = getCoordinate();  // OK
(double x, y) = getCoordinate();  // OK, types are specified explicitly
@dsimcha

What about functions that return multiple values of different types? Would this work?

(double x, int y) = fun();
@9rnsr
Collaborator

Yes. In your case, fun() should return tuple type, and

(double x, int y) = fun();

is just translated to

auto __tup = fun();
double x = __tup[0];  // __tup[0] has a type that is implicitly convertible to double
int y = __tup[1];     // ditto

Therefore, fun() can return Tuple!(int, int) or Tuple!(double, int).

@dsimcha

I love this idea. Is there any language design-level reason why it shouldn't be included or are we just waiting for Walter to get around to merging it?

@9rnsr 9rnsr closed this
@9rnsr 9rnsr reopened this
@nazriel

Bump, this is really sexy.
Same question as @dsimcha : Is there any language design-level reason why it shouldn't be included?

@dsimcha

On looking at this again one concern I see is the implicit temporary. This is caused by creating __tup and would cause postblits to run an extra time in cases where structs are being copied. This may not matter, though, since we're trying to discourage designs where structs can be arbitrarily expensive to copy.

@Dav1dde

Bump, any news on this?

@nazriel

@WalterBright what ya think about this?
@9rnsr has done amazing job to make work with Tuples great experience!

@dcousens

Bump, really looking forward to seeing this merged in. The simplicity of tuples is not going away :).

@TheNumb

Bump, bump, bump. Do want!

@robik

This is just awesome.

I have a question, does it work on function declarations? Like public (int, int) getCoordinates() ?

@nazriel

@robik I don't think so, but probably @9rnsr will know better

@9rnsr
Collaborator

@robik Unfortunately, no. This enhancement just supports distributing tuple fields to each variables in initializing.
I think your implication is "returning multiple values from function without packing (like Tuple!(int, int))", and it is completely different thing. And it's hard to implement, because it requires ABI change.

@repeatedly

LGTM :+1:

@9rnsr 9rnsr referenced this pull request in D-Programming-Language/dlang.org
Closed

[enh] Documentation for Multiple variable declaration #160

@meh

+1.

@9rnsr
Collaborator

Documentation for this enhancement: D-Programming-Language/dlang.org#160

@andralex
Owner

Just to give a bit of feedback - we're looking into this. If we conclude that this language addition solves most of the issues with tuples (so no radical redesign is needed), we'll pull this in. We don't want to pull this thinking that it might hurt a better future design.

@9rnsr
Collaborator

We don't want to pull this thinking that it might hurt a better future design.

What might be hurt in the future? Syntax? Semantics? or ABI issue?
I'd like to know that you worried in current.

@andralex
Owner

Syntax and semantics mostly. Walter and I are worried about overarching design more than anything. Consider that the "perfect" tuples for D are six good design decisions away. This pull request deals with destructuring and makes one decision, which is shaping all future decisions. If we later conclude tuples need some additional form of destructuring, or something different entirely, we'll have to add that too. And then some other stuff on top of that. So instead of making one move at a time and then analyzing the result, we want to think the strategy forward so as to have a good overal design, not (only) a good design for destructuring.

Now, it's quite possible that destructuring is about all we need, or that whatever else we need doesn't interact badly with destructuring. In that case this request will be pulled, and everybody will be happy.

@9rnsr
Collaborator

Thanks for your answer, @andralex . I cannot reply to your worry immediately, but I also would like to think about it.

@AndrejMitrovic
Collaborator

I'd like to be able to ignore a return value if that's possible. For example

(int a, void) = fun() -> discards second value

That way I don't have to forcefully introduce new symbol names into the current scope (using _ is a nogo because there might already be such a symbol, e.g. in foreach loops or a previous tuple destruction).

@braddr braddr referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
@WalterBright

I agree with Andrei, while I don't see anything obviously wrong with this proposal, I'd like to see a complete tuple design before committing ourselves to one aspect of it.

AndrejMitrovic and others added some commits
@AndrejMitrovic AndrejMitrovic Fixes Issue 9136 - Add isNested trait for aggregates and functions. 6ee244b
@9rnsr 9rnsr fix Issue 8902 - Unexpected "duplicate union initialization for X" error 840d88a
@yebblies yebblies Remove unused struct 'Environment' 1b7f937
@yebblies yebblies ddoc strings const correct 4d2aaa3
@yebblies yebblies Explicity take address of function 10b1ecd
@yebblies yebblies Don't take address of function without & fc89fa4
@yebblies yebblies Remove unimplemented method 001a936
@yebblies yebblies Copy-paste error 31129aa
@WalterBright WalterBright Merge pull request #1720 from yebblies/globalcopypaste
Copy-paste error
ce3c4ec
@WalterBright WalterBright Merge pull request #1719 from yebblies/unimpprop
Remove unimplemented method
0b5cdfb
@WalterBright WalterBright Merge pull request #1717 from yebblies/ddocconst
ddoc strings const correct
8ce00b3
@WalterBright WalterBright Merge pull request #1715 from yebblies/removeenv
Remove unused struct 'Environment'
7f23ab1
@WalterBright WalterBright Merge pull request #1718 from yebblies/takeaddress
Take address explicitly
4ce6282
@yebblies yebblies Merge pull request #1721 from AndrejMitrovic/Fix9635
Issue 9635 - Improve message on missing 'this' diagnostics.
d6db1b8
@WalterBright WalterBright Merge pull request #1369 from 9rnsr/fix8902
Issue 8902 - Unexpected "duplicate union initialization for X" error
a975ed6
@MartinNowak MartinNowak Merge pull request #1362 from AndrejMitrovic/IsNestedTrait
Issue 9136 - Add isNested trait for aggregates and functions
051c8be
@9rnsr 9rnsr Add TypeNone for type inference 5054891
@9rnsr 9rnsr isParameters now can check needId f035d61
@9rnsr 9rnsr Separate statement level and decldefs level cd6ece1
@9rnsr 9rnsr Update parser 12524be
@9rnsr 9rnsr Update initializer expansion process 3128f85
@9rnsr 9rnsr Add test cases 8e78e6d
@9rnsr 9rnsr Remove array expansion feature on initializer e748243
@9rnsr 9rnsr closed this
@AlexeyProkhin AlexeyProkhin referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 27, 2013
  1. @AndrejMitrovic
Commits on Feb 28, 2013
  1. @9rnsr
Commits on Mar 5, 2013
  1. @yebblies
  2. @yebblies
  3. @yebblies
  4. @yebblies
  5. @yebblies
  6. @yebblies

    Copy-paste error

    yebblies authored
  7. @WalterBright
  8. @WalterBright

    Merge pull request #1719 from yebblies/unimpprop

    WalterBright authored
    Remove unimplemented method
  9. @WalterBright

    Merge pull request #1717 from yebblies/ddocconst

    WalterBright authored
    ddoc strings const correct
  10. @WalterBright

    Merge pull request #1715 from yebblies/removeenv

    WalterBright authored
    Remove unused struct 'Environment'
  11. @WalterBright

    Merge pull request #1718 from yebblies/takeaddress

    WalterBright authored
    Take address explicitly
  12. @yebblies

    Merge pull request #1721 from AndrejMitrovic/Fix9635

    yebblies authored
    Issue 9635 - Improve message on missing 'this' diagnostics.
  13. @WalterBright

    Merge pull request #1369 from 9rnsr/fix8902

    WalterBright authored
    Issue 8902 - Unexpected "duplicate union initialization for X" error
  14. @MartinNowak

    Merge pull request #1362 from AndrejMitrovic/IsNestedTrait

    MartinNowak authored
    Issue 9136 - Add isNested trait for aggregates and functions
Commits on Mar 6, 2013
  1. @9rnsr

    Add TypeNone for type inference

    9rnsr authored
  2. @9rnsr
  3. @9rnsr
  4. @9rnsr

    Update parser

    9rnsr authored
  5. @9rnsr
  6. @9rnsr

    Add test cases

    9rnsr authored
  7. @9rnsr
This page is out of date. Refresh to see the latest.
View
18 src/declaration.c
@@ -994,7 +994,9 @@ void VarDeclaration::semantic(Scope *sc)
{
if (iexps->dim > nelems)
goto Lnomatch;
- if (e->type->implicitConvTo(arg->type))
+ if (arg->type->ty != Tnone && e->type->implicitConvTo(arg->type))
+ continue;
+ if (arg->type->ty == Tnone && iexps->dim == nelems)
continue;
}
@@ -1034,7 +1036,9 @@ void VarDeclaration::semantic(Scope *sc)
size_t iexps_dim = iexps->dim - 1 + exps->dim;
if (iexps_dim > nelems)
goto Lnomatch;
- if (ee->type->implicitConvTo(arg->type))
+ if (arg->type->ty != Tnone && ee->type->implicitConvTo(arg->type))
+ continue;
+ if (arg->type->ty == Tnone && iexps_dim == nelems)
continue;
if (expandAliasThisTuples(exps, u) != -1)
@@ -1056,6 +1060,13 @@ void VarDeclaration::semantic(Scope *sc)
if (iexps->dim < nelems)
goto Lnomatch;
+ for (size_t i = 0; i < iexps->dim; i++)
+ {
+ Parameter *arg = Parameter::getNth(tt->arguments, i);
+ if (arg->type->ty != Tnone && !iexps->tdata()[i]->type->implicitConvTo(arg->type))
+ goto Lnomatch;
+ }
+
ie = new TupleExp(init->loc, iexps);
}
Lnomatch:
@@ -1071,6 +1082,7 @@ void VarDeclaration::semantic(Scope *sc)
for (size_t i = 0; i < nelems; i++)
{ Parameter *arg = Parameter::getNth(tt->arguments, i);
+ Type *argtype = arg->type->ty == Tnone ? NULL : arg->type;
OutBuffer buf;
buf.printf("_%s_field_%llu", ident->toChars(), (ulonglong)i);
@@ -1087,7 +1099,7 @@ void VarDeclaration::semantic(Scope *sc)
{ ti = new ExpInitializer(einit->loc, einit);
}
- VarDeclaration *v = new VarDeclaration(loc, arg->type, id, ti);
+ VarDeclaration *v = new VarDeclaration(loc, argtype, id, ti);
if (arg->storageClass & STCparameter)
v->storage_class |= arg->storageClass;
//printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars());
View
10 src/doc.c
@@ -109,7 +109,7 @@ int isIdTail(unsigned char *p);
int isIndentWS(unsigned char *p);
int utfStride(unsigned char *p);
-static unsigned char ddoc_default[] = "\
+static const char ddoc_default[] = "\
DDOC = <html><head>\n\
<META http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n\
<title>$(TITLE)</title>\n\
@@ -200,11 +200,11 @@ ESCAPES = /</&lt;/\n\
/&/&amp;/\n\
";
-static char ddoc_decl_s[] = "$(DDOC_DECL ";
-static char ddoc_decl_e[] = ")\n";
+static const char ddoc_decl_s[] = "$(DDOC_DECL ";
+static const char ddoc_decl_e[] = ")\n";
-static char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
-static char ddoc_decl_dd_e[] = ")\n";
+static const char ddoc_decl_dd_s[] = "$(DDOC_DECL_DD ";
+static const char ddoc_decl_dd_e[] = ")\n";
/****************************************************
View
4 src/glue.c
@@ -36,10 +36,6 @@
#include "outbuf.h"
#include "irstate.h"
-struct Environment;
-
-Environment *benv;
-
void slist_add(Symbol *s);
void slist_reset();
void clearStringTab();
View
1  src/idgen.c
@@ -307,6 +307,7 @@ Msgtable msgtable[] =
{ "isAssociativeArray" },
{ "isFinalClass" },
{ "isPOD" },
+ { "isNested" },
{ "isFloating" },
{ "isIntegral" },
{ "isScalar" },
View
1  src/json.c
@@ -68,7 +68,6 @@ struct JsonOut
void property(const char *name, Type* type);
void property(const char *name, const char *deconame, Type* type);
void property(const char *name, Parameters* parameters);
- void property(const char *name, Expressions* expressions);
void property(const char *name, enum TRUST trust);
void property(const char *name, enum PURE purity);
void property(const char *name, enum LINK linkage);
View
2  src/mars.c
@@ -96,7 +96,7 @@ Global::Global()
#include "verstr.h"
;
- global.structalign = STRUCTALIGN_DEFAULT;
+ structalign = STRUCTALIGN_DEFAULT;
memset(&params, 0, sizeof(Param));
}
View
30 src/mtype.c
@@ -187,7 +187,6 @@ void Type::init()
mangleChar[Ttypedef] = 'T';
mangleChar[Tdelegate] = 'D';
- mangleChar[Tnone] = 'n';
mangleChar[Tvoid] = 'v';
mangleChar[Tint8] = 'g';
mangleChar[Tuns8] = 'h';
@@ -223,8 +222,9 @@ void Type::init()
mangleChar[Tvector] = '@';
mangleChar[Tint128] = '@';
mangleChar[Tuns128] = '@';
+ mangleChar[Tnone] = '@';
- mangleChar[Tnull] = 'n'; // same as TypeNone
+ mangleChar[Tnull] = 'n';
for (size_t i = 0; i < TMAX; i++)
{ if (!mangleChar[i])
@@ -249,6 +249,9 @@ void Type::init()
}
basic[Terror] = new TypeError();
+ tnone = new TypeNone();
+ tnone->deco = tnone->merge()->deco;
+
tnull = new TypeNull();
tnull->deco = tnull->merge()->deco;
@@ -2259,6 +2262,23 @@ uinteger_t Type::sizemask()
return m;
}
+/* ============================= TypeNone =========================== */
+
+TypeNone::TypeNone()
+ : Type(Tnone)
+{
+}
+
+Type *TypeNone::syntaxCopy()
+{
+ return this;
+}
+
+void TypeNone::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs)
+{
+ buf->writestring("_none_");
+}
+
/* ============================= TypeError =========================== */
TypeError::TypeError()
@@ -8116,11 +8136,14 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc)
// return defaultInit(loc);
Expressions *structelems = new Expressions();
structelems->setDim(sym->fields.dim - sym->isnested);
+ unsigned offset = 0;
for (size_t j = 0; j < structelems->dim; j++)
{
VarDeclaration *vd = sym->fields[j];
Expression *e;
- if (vd->init)
+ if (vd->offset < offset)
+ e = NULL;
+ else if (vd->init)
{ if (vd->init->isVoidInitializer())
e = NULL;
else
@@ -8145,6 +8168,7 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc)
e = e->implicitCastTo(vd->scope, telem);
}
+ offset = vd->offset + vd->type->size();
(*structelems)[j] = e;
}
StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems);
View
9 src/mtype.h
@@ -182,6 +182,7 @@ struct Type : Object
static Type *tvoidptr; // void*
static Type *tstring; // immutable(char)[]
static Type *tvalist; // va_list alias
+ #define tnone basic[Tnone] // for type inference
#define terror basic[Terror] // for error recovery
#define tnull basic[Tnull] // for null type
@@ -345,6 +346,14 @@ struct Type : Object
virtual TypeBasic *isTypeBasic();
};
+struct TypeNone : Type
+{
+ TypeNone();
+
+ Type *syntaxCopy();
+ void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs);
+};
+
struct TypeError : Type
{
TypeError();
View
6 src/optimize.c
@@ -801,19 +801,19 @@ Expression *shift_optimize(int result, BinExp *e, Expression *(*shift)(Type *, E
Expression *ShlExp::optimize(int result, bool keepLvalue)
{
//printf("ShlExp::optimize(result = %d) %s\n", result, toChars());
- return shift_optimize(result, this, Shl);
+ return shift_optimize(result, this, &Shl);
}
Expression *ShrExp::optimize(int result, bool keepLvalue)
{
//printf("ShrExp::optimize(result = %d) %s\n", result, toChars());
- return shift_optimize(result, this, Shr);
+ return shift_optimize(result, this, &Shr);
}
Expression *UshrExp::optimize(int result, bool keepLvalue)
{
//printf("UshrExp::optimize(result = %d) %s\n", result, toChars());
- return shift_optimize(result, this, Ushr);
+ return shift_optimize(result, this, &Ushr);
}
Expression *AndExp::optimize(int result, bool keepLvalue)
View
274 src/parse.c
@@ -63,6 +63,7 @@ Parser::Parser(Module *module, unsigned char *base, size_t length, int doDocComm
linkage = LINKd;
endloc = 0;
inBrackets = 0;
+ inStatements = 0;
lookingForElse = 0;
//nextToken(); // start up the scanner
}
@@ -154,6 +155,8 @@ Dsymbols *Parser::parseDeclDefs(int once)
Condition *condition;
unsigned char *comment;
+ inStatements = 0;
+
//printf("Parser::parseDeclDefs()\n");
decldefs = new Dsymbols();
do
@@ -230,6 +233,7 @@ Dsymbols *Parser::parseDeclDefs(int once)
case TOKunion:
case TOKclass:
case TOKinterface:
+ case TOKlparen:
Ldeclaration:
a = parseDeclarations(STCundefined, NULL);
decldefs->append(a);
@@ -421,8 +425,10 @@ Dsymbols *Parser::parseDeclDefs(int once)
case TOKimmutable:
case TOKwild:
// If followed by a (, it is not a storage class
- if (peek(&token)->value == TOKlparen)
- break;
+ Token *tk;
+ if ((tk = peek(&token))->value == TOKlparen)
+ if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
+ break;
if (token.value == TOKconst)
stc = STCconst;
else if (token.value == TOKshared)
@@ -475,9 +481,23 @@ Dsymbols *Parser::parseDeclDefs(int once)
continue;
}
- /* Look for return type inference for template functions.
+ /* Look for multiple var initializers:
+ * storage_class ( identifier, ... ) = initializer;
*/
Token *tk;
+ if (storageClass &&
+ token.value == TOKlparen &&
+ peekNext() != TOKrparen &&
+ (tk = &token, skipParens(tk, &tk)) &&
+ peek(tk)->value == TOKassign)
+ {
+ a = parseMultiVarDeclaration(storageClass, comment);
+ decldefs->append(a);
+ continue;
+ }
+
+ /* Look for return type inference for template functions.
+ */
if (token.value == TOKidentifier &&
(tk = peek(&token))->value == TOKlparen &&
skipParens(tk, &tk) &&
@@ -2830,6 +2850,8 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
Loc loc = this->loc;
Expressions *udas = NULL;
+ Token *tk;
+
//printf("parseDeclarations() %s\n", token.toChars());
if (!comment)
comment = token.blockComment;
@@ -2922,6 +2944,10 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
tok = token.value;
nextToken();
break;
+ case TOKlparen:
+ if (peek(&token)->value != TOKrparen)
+ return parseMultiVarDeclaration(storage_class, comment);
+ break;
default: break;
}
@@ -2931,29 +2957,33 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
switch (token.value)
{
case TOKconst:
- if (peek(&token)->value == TOKlparen)
- break; // const as type constructor
+ if ((tk = peek(&token))->value == TOKlparen)
+ if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
+ break; // const as type constructor
stc = STCconst; // const as storage class
goto L1;
case TOKinvariant:
case TOKimmutable:
- if (peek(&token)->value == TOKlparen)
- break;
+ if ((tk = peek(&token))->value == TOKlparen)
+ if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
+ break;
if (token.value == TOKinvariant)
deprecation("use of 'invariant' rather than 'immutable' is deprecated");
stc = STCimmutable;
goto L1;
case TOKshared:
- if (peek(&token)->value == TOKlparen)
- break;
+ if ((tk = peek(&token))->value == TOKlparen)
+ if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
+ break;
stc = STCshared;
goto L1;
case TOKwild:
- if (peek(&token)->value == TOKlparen)
- break;
+ if ((tk = peek(&token))->value == TOKlparen)
+ if (skipParens(tk, &tk) && peek(tk)->value != TOKassign)
+ break;
stc = STCwild;
goto L1;
@@ -3075,10 +3105,26 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c
return parseAutoDeclarations(storage_class, comment);
}
+ /* Look for multiple var declaration:
+ * storage_class ( identifier, ... ) = initializer;
+ */
+ if (storage_class &&
+ token.value == TOKlparen &&
+ peekNext() != TOKrparen &&
+ (tk = &token, skipParens(tk, &tk)) &&
+ (peek(tk)->value == TOKassign))
+ {
+ if (udas)
+ {
+ // Need to improve this
+ error("user defined attributes not allowed for auto declarations");
+ }
+ return parseMultiVarDeclaration(storage_class, comment);
+ }
+
/* Look for return type inference for template functions.
*/
{
- Token *tk;
if (storage_class &&
token.value == TOKidentifier &&
(tk = peek(&token))->value == TOKlparen &&
@@ -3315,6 +3361,160 @@ Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, unsigned char
}
#endif
+#if DMDV2
+Dsymbols *makeMultiVarDeclaration(Loc loc, Parameters *params, Initializer *init, int inStatements)
+{
+#if 0
+ for (size_t i = 0; i < params->dim; i++)
+ {
+ Parameter *prm = params->tdata()[i];
+ printf("prms[%d] = %s %s, stc = %llx\n", i,
+ (prm->type ? prm->type->toChars() : "(null)"),
+ prm->ident->toChars(),
+ prm->storageClass);
+ }
+ printf("init = %s\n", init->toChars());
+#endif
+
+ Dsymbols *a = new Dsymbols();
+
+ for (size_t i = 0; i < params->dim; i++)
+ {
+ Parameter *prm = params->tdata()[i];
+ if (!prm->type)
+ prm->type = Type::tnone;
+ }
+
+ Expression *ve;
+ if (inStatements)
+ {
+ TypeTuple *tt = new TypeTuple(params);
+ Identifier *id = Lexer::uniqueId("__tup");
+ VarDeclaration *v = new VarDeclaration(loc, tt, id, init);
+ a->push(v);
+
+ ve = new IdentifierExp(loc, id);
+ }
+ else
+ ve = init->toExpression();
+ assert(ve);
+
+ for (size_t i = 0; i < params->dim; i++)
+ {
+ Expressions *exps = new Expressions();
+ exps->push(new IntegerExp(i));
+ ExpInitializer *ei = new ExpInitializer(loc, new ArrayExp(loc, ve, exps));
+
+ Parameter *prm = params->tdata()[i];
+ VarDeclaration *v = new VarDeclaration(loc, NULL, prm->ident, ei);
+ v->storage_class |= prm->storageClass;
+ if (inStatements)
+ v->storage_class |= STCref | STCforeach;
+ a->push(v);
+ }
+
+ return a;
+}
+
+Dsymbols *Parser::parseMultiVarDeclaration(StorageClass storageClass, unsigned char *comment)
+{
+ //printf("parseMultiVarDeclaration, stc = %x\n", storageClass);
+
+ Parameters *prms = new Parameters();
+ Loc loc = this->loc;
+
+ // ( StorageClass TupleTypeList ) = Initializer ;
+ // StorageClasses ( TupleTypeList ) = Initializer ;
+ check(TOKlparen);
+ while (1)
+ {
+ if (token.value == TOKrparen)
+ break;
+
+ Identifier *ai = NULL;
+ Type *at;
+
+ StorageClass storage_class = storageClass;
+ StorageClass stc;
+ while (1)
+ {
+ switch (token.value)
+ {
+ case TOKconst:
+ if (peek(&token)->value == TOKlparen)
+ break; // const as type constructor
+ stc = STCconst; // const as storage class
+ goto L1;
+
+ case TOKinvariant:
+ case TOKimmutable:
+ if (peek(&token)->value == TOKlparen)
+ break;
+ stc = STCimmutable;
+ goto L1;
+
+ case TOKshared:
+ if (peek(&token)->value == TOKlparen)
+ break;
+ stc = STCshared;
+ goto L1;
+
+ case TOKwild:
+ if (peek(&token)->value == TOKlparen)
+ break;
+ stc = STCwild;
+ goto L1;
+
+ // case TOKstatic: stc = STCstatic; goto L1;
+ case TOKauto: stc = STCauto; goto L1;
+ case TOKscope: stc = STCscope; goto L1;
+
+ L1:
+ if (storage_class & stc)
+ error("redundant storage class '%s'", token.toChars());
+ storage_class = storage_class | stc;
+ composeStorageClass(storage_class);
+ nextToken();
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ if (token.value == TOKidentifier)
+ {
+ Token *t = peek(&token);
+ if (t->value == TOKcomma || t->value == TOKrparen)
+ { ai = token.ident;
+ at = NULL; // infer argument type
+ nextToken();
+ goto Larg;
+ }
+ }
+ at = parseType(&ai);
+ if (!ai)
+ error("no identifier for declarator %s", at->toChars());
+ Larg:
+ Parameter *a = new Parameter(storage_class, at, ai, NULL);
+ prms->push(a);
+ if (token.value == TOKcomma)
+ nextToken();
+ }
+ check(TOKrparen);
+ check(TOKassign);
+
+ Initializer *init = parseInitializer();
+
+ if (token.value != TOKsemicolon)
+ error("semicolon expected following tuple declaration, not '%s'", token.toChars());
+ nextToken();
+
+ return makeMultiVarDeclaration(loc, prms, init, inStatements);
+}
+#endif
+
/*****************************************
* Parse contracts following function declaration.
*/
@@ -3687,6 +3887,8 @@ Statement *Parser::parseStatement(int flags)
bool isfinal;
Loc loc = this->loc;
+ inStatements = 1;
+
//printf("parseStatement()\n");
if (flags & PScurly && token.value != TOKlcurly)
@@ -3720,6 +3922,30 @@ Statement *Parser::parseStatement(int flags)
goto Lexp;
break;
+ case TOKlparen:
+ {
+ Token *t = &token;
+ switch (peek(&token)->value)
+ {
+ case TOKrparen:
+ break;
+
+ case TOKconst:
+ case TOKinvariant:
+ case TOKimmutable:
+ case TOKshared:
+ case TOKwild:
+
+ // case TOKstatic:
+ case TOKauto:
+ case TOKscope:
+ goto Ldeclaration;
+
+ default:
+ if (isParameters(&t, 2) && t->value == TOKassign)
+ goto Ldeclaration;
+ }
+ }
case TOKassert:
case TOKthis:
case TOKsuper:
@@ -3740,7 +3966,6 @@ Statement *Parser::parseStatement(int flags)
case TOKtrue:
case TOKfalse:
case TOKstring:
- case TOKlparen:
case TOKcast:
case TOKmul:
case TOKmin:
@@ -4704,6 +4929,8 @@ Statement *Parser::parseStatement(int flags)
break;
}
+ inStatements = 0;
+
return s;
}
@@ -5088,7 +5315,13 @@ int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok)
}
-int Parser::isParameters(Token **pt)
+/************************************
+ * Input:
+ * needId 0 no identifier
+ * 1 identifier optional
+ * 2 must have identifier
+ */
+int Parser::isParameters(Token **pt, int needId)
{ // This code parallels parseParameters()
Token *t = *pt;
@@ -5156,10 +5389,19 @@ int Parser::isParameters(Token **pt)
{ if (!isBasicType(&t))
return FALSE;
L2:
- int tmp = FALSE;
+ int haveId = 0;
if (t->value != TOKdotdotdot &&
- !isDeclarator(&t, &tmp, TOKreserved))
+ !isDeclarator(&t, &haveId, TOKreserved))
return FALSE;
+
+ if ( needId == 1 ||
+ (needId == 0 && !haveId) ||
+ (needId == 2 && haveId))
+ {
+ }
+ else
+ return FALSE;
+
if (t->value == TOKassign)
{ t = peek(t);
if (!isExpression(&t))
View
4 src/parse.h
@@ -66,6 +66,7 @@ struct Parser : Lexer
enum LINK linkage;
Loc endloc; // set to location of last right curly
int inBrackets; // inside [] of array index or slice
+ int inStatements; // inside runnable code
Loc lookingForElse; // location of lonely if looking for an else
Parser(Module *module, unsigned char *base, size_t length, int doDocComment);
@@ -73,6 +74,7 @@ struct Parser : Lexer
Dsymbols *parseModule();
Dsymbols *parseDeclDefs(int once);
Dsymbols *parseAutoDeclarations(StorageClass storageClass, unsigned char *comment);
+ Dsymbols *parseMultiVarDeclaration(StorageClass storageClass, unsigned char *comment);
Dsymbols *parseBlock();
void composeStorageClass(StorageClass stc);
StorageClass parseAttribute(Expressions **pexps);
@@ -124,7 +126,7 @@ struct Parser : Lexer
int isDeclaration(Token *t, int needId, enum TOK endtok, Token **pt);
int isBasicType(Token **pt);
int isDeclarator(Token **pt, int *haveId, enum TOK endtok);
- int isParameters(Token **pt);
+ int isParameters(Token **pt, int needId = 1);
int isExpression(Token **pt);
int skipParens(Token *t, Token **pt);
int skipAttributes(Token *t, Token **pt);
View
30 src/traits.c
@@ -168,6 +168,34 @@ Expression *TraitsExp::semantic(Scope *sc)
}
goto Ltrue;
}
+ else if (ident == Id::isNested)
+ {
+ if (dim != 1)
+ goto Ldimerror;
+ Object *o = (*args)[0];
+ Dsymbol *s = getDsymbol(o);
+ AggregateDeclaration *a;
+ FuncDeclaration *f;
+
+ if (!s) { }
+ else if ((a = s->isAggregateDeclaration()) != NULL)
+ {
+ if (a->isnested)
+ goto Ltrue;
+ else
+ goto Lfalse;
+ }
+ else if ((f = s->isFuncDeclaration()) != NULL)
+ {
+ if (f->isNested())
+ goto Ltrue;
+ else
+ goto Lfalse;
+ }
+
+ error("aggregate or function expected instead of '%s'", o->toChars());
+ goto Lfalse;
+ }
else if (ident == Id::isAbstractFunction)
{
FuncDeclaration *f;
@@ -379,7 +407,7 @@ Expression *TraitsExp::semantic(Scope *sc)
p.exps = exps;
p.e1 = e;
p.ident = ident;
- overloadApply(f, fptraits, &p);
+ overloadApply(f, &fptraits, &p);
TupleExp *tup = new TupleExp(loc, exps);
return tup->semantic(sc);
View
218 test/runnable/multivar.d
@@ -0,0 +1,218 @@
+
+extern (C) int printf(const(char*) fmt, ...);
+
+struct Tup(T...)
+{
+ T field;
+ alias field this;
+
+ bool opEquals()(auto ref Tup rhs) const
+ {
+ foreach (i, _; T)
+ if (field[i] != rhs.field[i])
+ return false;
+ return true;
+ }
+}
+
+Tup!T tup(T...)(T fields)
+{
+ return typeof(return)(fields);
+}
+
+template Seq(T...)
+{
+ alias T Seq;
+}
+
+/**********************************************/
+
+void test1()
+{
+ auto (num, str) = Seq!(10, "str");
+ assert(num == 10);
+ assert(str == "str");
+
+ int eval = 0;
+ auto (n, t) = (eval++, tup(10, tup("str", [1,2])));
+ assert(eval == 1);
+ assert(n == 10);
+ eval = 0;
+ auto (s, a) = (eval++, t);
+ assert(eval == 1);
+ assert(s == "str");
+ assert(a == [1,2]);
+
+ auto (i, j) = 10;
+ assert(i == 10);
+ assert(j == 10);
+
+ auto (t1, t2) = tup(1, "str", [1,2]);
+ assert(t1 == tup(1, "str", [1,2]));
+ assert(t2 == tup(1, "str", [1,2]));
+
+ auto (x) = 1;
+ assert(x == 1);
+}
+
+/**********************************************/
+
+void test2()
+{
+ //auto (n, m) = [1,2];
+ //assert(n == 1);
+ //assert(m == 2);
+
+ //int[2] sa = [1,2];
+ //auto (x, y) = sa;
+ //assert(x == 1);
+ //assert(y == 2);
+}
+
+/**********************************************/
+
+void test3()
+{
+ alias tup tuple;
+ alias Seq TypeTuple;
+
+ // Example of 1
+ {
+ auto (i, j) = tuple(10, "a");
+ static assert(is(typeof(i) == int));
+ static assert(is(typeof(j) == string));
+
+ const (x, y) = TypeTuple!(1, 2);
+ static assert(is(typeof(x) == const(int)));
+ static assert(is(typeof(y) == const(int)));
+ }
+
+ {
+ // Example of 2-1
+ (int i, string j) = tuple(10, "a");
+ static assert(is(typeof(i) == int));
+ static assert(is(typeof(j) == string));
+
+ // Example of 2-2
+ (auto c, r) = TypeTuple!('c', "har");
+ static assert(is(typeof(c) == char));
+ static assert(is(typeof(r) == string));
+
+ (const x, auto y) = TypeTuple!(1, 2);
+ static assert(is(typeof(x) == const(int)));
+ static assert(is(typeof(y) == int));
+
+ (auto a1, const int[] a2) = TypeTuple!([1], [2,3]);
+ static assert(is(typeof(a1) == int[]));
+ static assert(is(typeof(a2) == const(int[])));
+ }
+}
+
+/**********************************************/
+
+void test4()
+{
+ auto (x, y, z) = Seq!(10, tup("a", [1,2]));
+ assert(x == 10);
+ assert(y == "a");
+ assert(z == [1,2]);
+}
+
+/**********************************************/
+
+void test5()
+{
+ auto t = tup(10, "a");
+
+ auto (a1) = t[0..1];
+ assert(a1 == 10);
+
+ (auto a2) = t[0..1];
+ assert(a2 == 10);
+
+ //auto (x1) = [10];
+ //assert(x1 == 10);
+
+ (auto x2) = tup(10);
+ assert(x2 == 10);
+
+ (auto x3) = tup(10)[0..1];
+ assert(x3 == 10);
+
+ (int x4) = Seq!(10);
+ assert(x4 == 10);
+
+ // isolated comma parsing
+ (int n,) = tup(10);
+ assert(n == 10);
+
+ auto (x, y,) = tup(10, 20);
+ assert(x == 10);
+ assert(y == 20);
+}
+
+/**********************************************/
+
+void test6()
+{
+ const(int x, string y) = tup(10, "a");
+ static assert(is(typeof(x) == const(int)));
+ static assert(is(typeof(y) == const(string)));
+ assert(x == 10);
+ assert(y == "a");
+
+ (int i, double j, string k) = tup(tup(10, 2.2), "a");
+ static assert(is(typeof(i) == int));
+ static assert(is(typeof(j) == double));
+ static assert(is(typeof(k) == string));
+ assert(i == 10);
+ assert(j == 2.2);
+ assert(k == "a");
+}
+
+/**********************************************/
+
+Tup!(int, string) func7(){ return tup(10, "str"); }
+
+auto (num71, str71) = func7();
+const (num72, str72) = Seq!(10, "str");
+
+enum (num73, str73) = func7();
+
+void test7()
+{
+ assert(num71 == 10);
+ assert(str71 == "str");
+ num71 = 20;
+ str71 = "hello";
+
+ assert(num72 == 10);
+ assert(str72 == "str");
+ static assert(!__traits(compiles, num72 = 20));
+ static assert(!__traits(compiles, str72 = "hello"));
+ auto pnum72 = &num72;
+ auto pstr72 = &str72;
+
+ static assert(num73 == 10);
+ static assert(str73 == "str");
+ static assert(!__traits(compiles, num73 = 20));
+// static assert(!__traits(compiles, str73 = "hello"));
+ static assert(!__traits(compiles, &num73));
+// static assert(!__traits(compiles, &str73));
+}
+
+/**********************************************/
+
+int main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+
+ printf("Success\n");
+ return 0;
+}
View
19 test/runnable/structlit.d
@@ -589,6 +589,24 @@ void test8763()
}
/********************************************/
+// 8902
+
+union U8902 { int a, b; }
+
+enum U8902 u8902a = U8902.init; // No errors
+U8902 u8902b; // No errors
+U8902 u8902c = U8902.init; // Error: duplicate union initialization for b
+
+void test8902()
+{
+ U8902 u8902d = U8902.init; // No errors
+ immutable U8902 u8902e = U8902.init; // No errors
+ immutable static U8902 u8902f = U8902.init; // Error: duplicate union...
+ static U8902 u8902g = u8902e; // Error: duplicate union...
+ static U8902 u8902h = U8902.init; // Error: duplicate union...
+}
+
+/********************************************/
// 9116
void test9116()
@@ -672,6 +690,7 @@ int main()
test7929();
test7021();
test8763();
+ test8902();
test9116();
test9293();
test9566();
View
30 test/runnable/traits.d
@@ -1088,6 +1088,35 @@ void test9552()
/*************************************************************/
+void test9136()
+{
+ int x;
+ struct S1 { void f() { x++; } }
+ struct U1 { void f() { x++; } }
+ static struct S2 { }
+ static struct S3 { S1 s; }
+ static struct U2 { }
+ void f1() { x++; }
+ static void f2() { }
+
+ static assert(__traits(isNested, S1));
+ static assert(__traits(isNested, U1));
+ static assert(!__traits(isNested, S2));
+ static assert(!__traits(isNested, S3));
+ static assert(!__traits(isNested, U2));
+ static assert(!__traits(compiles, __traits(isNested, int) ));
+ static assert(!__traits(compiles, __traits(isNested, f1, f2) ));
+ static assert(__traits(isNested, f1));
+ static assert(!__traits(isNested, f2));
+
+ static class A { static class SC { } class NC { } }
+ static assert(!__traits(isNested, A));
+ static assert(!__traits(isNested, A.SC));
+ static assert(__traits(isNested, A.NC));
+}
+
+/********************************************************/
+
int main()
{
test1();
@@ -1121,6 +1150,7 @@ int main()
test5978();
test7408();
test9552();
+ test9136();
writeln("Success");
return 0;
Something went wrong with that request. Please try again.