Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8360ff7
Parse UnpackDeclaration.
tgehr May 5, 2018
cff9cca
Semantic analysis for UnpackDeclaration.
tgehr May 7, 2018
63663fd
Add UnpackDeclaration to Parameter.
tgehr Sep 1, 2023
3ff3977
Parse UnpackDeclaration as foreach variable.
tgehr Sep 1, 2023
3342ee0
Semantic analysis for UnpackDeclaration in foreach variable.
tgehr Sep 1, 2023
955c482
Parse UnpackDeclaration as a function literal parameter.
tgehr Sep 4, 2024
0a41ca9
Semantic analysis for UnpackDeclaration in function literal parameter.
tgehr Sep 4, 2024
6bf7925
[unpacking] Add test
ntrel Apr 7, 2025
95e9d02
[unpacking] Add `-preview=tuples` CLI switch
ntrel Apr 13, 2025
8a1d59c
[unpacking] Tweak lone identifier error message, add test
ntrel Apr 14, 2025
31c1a2b
[unpacking] Fix parsing malformed auto declarations starting with `(`…
ntrel Apr 14, 2025
1b0aa19
Test extern/scope unpacking.
tgehr Apr 14, 2025
5f77d4c
[unpacking] Add test for missing identifier in unpack declaration (#9)
ntrel Apr 14, 2025
2f75ede
[unpacking] Test semantic errors, tweak RHS mismatch error (#10)
ntrel Apr 14, 2025
ee4d413
[unpacking] Add tests for invalid parameter storage classes (#11)
ntrel Apr 18, 2025
33038aa
[unpacking] Support `auto (a, int b) =` (#13)
ntrel Apr 25, 2025
92ded5e
[unpacking] Error when unpacking declaration itself has a UDA
ntrel May 15, 2025
52e63da
Tweak unpack declaration LOC
ntrel May 15, 2025
b2a36e4
Fix ICE in tuple unpack declaration with moved value
MetaLang Jun 20, 2026
65b37ec
Add changelog entry for new tuple unpacking feature
MetaLang Jun 25, 2026
5475d5b
Disallow `(T x,) = tup, i = 1;`
ntrel Jun 25, 2026
34a88c8
[spec/declaration] Unpacking declarations
ntrel Jun 24, 2026
e31bf15
Change markdown examples to ddoc style `---` for spec consistency
ntrel Jun 26, 2026
e09b209
Tweak wording for pattern without storage class
ntrel Jun 27, 2026
d31d660
Fix AssignExpression links
ntrel Jun 28, 2026
0b09123
Fix lex, type links
ntrel Jun 29, 2026
459c326
[spec/statement] Unpacking Foreach Variables
ntrel Jun 27, 2026
1d0673a
[spec/function] Unpacking Parameters
ntrel Jun 28, 2026
d3ad684
[changelog] Mention tuple parameters
ntrel Jun 29, 2026
e4413f6
Mention unpacking in function literal templates
ntrel Jun 29, 2026
4268368
Fix whitespace.
tgehr Jul 6, 2026
c5c73ef
Update `globals.h`.
tgehr Jul 6, 2026
5a3940b
Fix cxxfrontend tests.
tgehr Jul 6, 2026
db24bf7
Fix Ddoc warning.
tgehr Jul 6, 2026
99ac870
Try to appease D-Scanner.
tgehr Jul 6, 2026
682116c
Refactor preview flag so parser does not import globals.
tgehr Jul 7, 2026
c757bcc
Improve style.
tgehr Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions changelog/dmd.tuple-unpacking.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
Tuple unpacking is now supported!

D now supports tuple unpacking for variable declarations, `foreach` loops, and function literal
template parameters.
This feature is available as a preview and can be enabled with the `-preview=tuples` compiler switch.
It allows extracting multiple values from a tuple or compile-time sequence directly into distinct variables.

-------
// Requires -preview=tuples
import std.typecons : tuple;

void main()
{
auto (x, y, z) = tuple(1, 2.5, "three");
assert(x == 1);
assert(y == 2.5);
assert(z == "three");

// Types can be specified explicitly
(int a, string b) = tuple(4, "five");

// Unpacking works in foreach loops
foreach ((i, s); [tuple(1, "one"), tuple(2, "two")])
{
// ...
}
}
-------

For more information, see the D blog article: $(LINK2 https://blog.dlang.org/2026/04/12/dip-1053-a-tale-of-tuples/, DIP 1053 - A Tale of Tuples).
20 changes: 20 additions & 0 deletions compiler/include/dmd/attrib.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,23 @@ class UserAttributeDeclaration final : public AttribDeclaration
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
};

/***********************************************************
* Unpack declarations look like, e.g.:
* auto (a, b) = init;
* (int a, string b) = init;
*/
class UnpackDeclaration final : public AttribDeclaration
{
public:
Expression *_init;
ScopeDsymbol *scopesym;
StorageClass declared_storage_class;
StorageClass storage_class;
bool onStack;
bool lowered;

UnpackDeclaration *syntaxCopy(Dsymbol *) override;
const char *kind() const override;
void accept(Visitor *v) override { v->visit(this); }
};
2 changes: 2 additions & 0 deletions compiler/include/dmd/dsymbol.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ThisDeclaration;
class BitFieldDeclaration;
class TypeInfoDeclaration;
class TupleDeclaration;
class UnpackDeclaration;
class AliasDeclaration;
class AggregateDeclaration;
class EnumDeclaration;
Expand Down Expand Up @@ -266,6 +267,7 @@ class Dsymbol : public ASTNode
BitFieldDeclaration *isBitFieldDeclaration();
TypeInfoDeclaration *isTypeInfoDeclaration();
TupleDeclaration *isTupleDeclaration();
UnpackDeclaration *isUnpackDeclaration();
AliasDeclaration *isAliasDeclaration();
AggregateDeclaration *isAggregateDeclaration();
FuncDeclaration *isFuncDeclaration();
Expand Down
2 changes: 2 additions & 0 deletions compiler/include/dmd/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ struct Param
// Implementation: https://github.com/dlang/dmd/pull/9817
FeatureState safer; // safer by default (more @safe checks in unattributed code)
// https://github.com/WalterBright/documents/blob/38f0a846726b571f8108f6e63e5e217b91421c86/safer.md
FeatureState tuples; // Tuple unpacking

FeatureState noSharedAccess; // read/write access to shared memory objects
d_bool previewIn; // `in` means `[ref] scope const`, accepts rvalues
Expand Down Expand Up @@ -332,6 +333,7 @@ struct CompileEnv
DString time;
DString vendor;
DString timestamp;
d_bool tuples;
d_bool previewIn;
d_bool transitionIn;
d_bool ddocOutput;
Expand Down
6 changes: 5 additions & 1 deletion compiler/include/dmd/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class TemplateDeclaration;
class TypeBasic;
class Parameter;

class UnpackDeclaration;

// Back end
#ifdef IN_GCC
typedef union tree_node type;
Expand Down Expand Up @@ -428,9 +430,11 @@ class Parameter final : public ASTNode
Identifier *ident;
Expression *defaultArg;
UserAttributeDeclaration *userAttribDecl; // user defined attributes
UnpackDeclaration *unpack;

static Parameter *create(Loc loc, StorageClass storageClass, Type *type, Identifier *ident,
Expression *defaultArg, UserAttributeDeclaration *userAttribDecl);
Expression *defaultArg, UserAttributeDeclaration *userAttribDecl,
UnpackDeclaration *unpack);
Parameter *syntaxCopy();
bool isLazy() const;
bool isReference() const;
Expand Down
2 changes: 2 additions & 0 deletions compiler/include/dmd/visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class StaticIfDeclaration;
class MixinDeclaration;
class StaticForeachDeclaration;
class UserAttributeDeclaration;
class UnpackDeclaration;
class ForwardingAttribDeclaration;

class ScopeDsymbol;
Expand Down Expand Up @@ -377,6 +378,7 @@ class ParseTimeVisitor
virtual void visit(StorageClassDeclaration *s) { visit((AttribDeclaration *)s); }
virtual void visit(ConditionalDeclaration *s) { visit((AttribDeclaration *)s); }
virtual void visit(StaticForeachDeclaration *s) { visit((AttribDeclaration *)s); }
virtual void visit(UnpackDeclaration *s) { visit((AttribDeclaration *)s); }

// Miscellaneous
virtual void visit(DeprecatedDeclaration *s) { visit((StorageClassDeclaration *)s); }
Expand Down
49 changes: 46 additions & 3 deletions compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,20 @@ struct ASTBase
{
v.visit(this);
}

extern (D) static Dsymbols* arraySyntaxCopy(Dsymbols* a)
{
Dsymbols* b = null;
if (a)
{
b = a.copy();
for (size_t i = 0; i < b.length; i++)
{
(*b)[i] = (*b)[i].syntaxCopy(null);
}
}
return b;
}
}

extern (C++) class AliasThis : Dsymbol
Expand Down Expand Up @@ -1346,6 +1360,33 @@ struct ASTBase
}
}

extern (C++) final class UnpackDeclaration : AttribDeclaration
{
Dsymbols* vars;
Expression _init;
StorageClass storage_class;

extern (D) this(Loc loc, Dsymbols* vars, Expression _init, StorageClass storage_class)
{
super(null);
this.loc = loc;
this.vars = vars;
this._init = _init;
this.storage_class = storage_class;
}

override UnpackDeclaration syntaxCopy(Dsymbol s)
{
return new UnpackDeclaration(loc, Dsymbol.arraySyntaxCopy(vars), _init ? _init.syntaxCopy() : null, storage_class);
}

override void accept(Visitor v)
{
v.visit(this);
}
}


extern (C++) final class EnumMember : VarDeclaration
{
Expression origValue;
Expand Down Expand Up @@ -1703,16 +1744,18 @@ struct ASTBase
Identifier ident;
Expression defaultArg;
UserAttributeDeclaration userAttribDecl; // user defined attributes
UnpackDeclaration unpack;

extern (D) alias ForeachDg = int delegate(size_t idx, Parameter param);

final extern (D) this(Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl)
final extern (D) this(Loc loc, StorageClass storageClass, Type type, Identifier ident, Expression defaultArg, UserAttributeDeclaration userAttribDecl, UnpackDeclaration unpack)
{
this.storageClass = storageClass;
this.type = type;
this.ident = ident;
this.defaultArg = defaultArg;
this.userAttribDecl = userAttribDecl;
this.unpack = unpack;
}

static size_t dim(Parameters* parameters)
Expand Down Expand Up @@ -1779,7 +1822,7 @@ struct ASTBase

Parameter syntaxCopy()
{
return new Parameter(loc, storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? userAttribDecl.syntaxCopy(null) : null);
return new Parameter(loc, storageClass, type ? type.syntaxCopy() : null, ident, defaultArg ? defaultArg.syntaxCopy() : null, userAttribDecl ? userAttribDecl.syntaxCopy(null) : null, unpack ? unpack.syntaxCopy(null) : null);
}

override void accept(Visitor v)
Expand Down Expand Up @@ -3638,7 +3681,7 @@ struct ASTBase
Expression e = (*exps)[i];
if (e.type.ty == Ttuple)
error(e.loc, "cannot form sequence of sequences");
auto arg = new Parameter(e.loc, STC.none, e.type, null, null, null);
auto arg = new Parameter(e.loc, STC.none, e.type, null, null, null, null);
(*arguments)[i] = arg;
}
}
Expand Down
Loading
Loading