Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b8dce25
Parse UnpackDeclaration.
tgehr May 5, 2018
bdcfafe
Semantic analysis for UnpackDeclaration.
tgehr May 7, 2018
46877fb
Add UnpackDeclaration to Parameter.
tgehr Sep 1, 2023
3b3c828
Parse UnpackDeclaration as foreach variable.
tgehr Sep 1, 2023
3887a3d
Semantic analysis for UnpackDeclaration in foreach variable.
tgehr Sep 1, 2023
7a352f2
Parse UnpackDeclaration as a function literal parameter.
tgehr Sep 4, 2024
47743e0
Semantic analysis for UnpackDeclaration in function literal parameter.
tgehr Sep 4, 2024
eb1145b
[unpacking] Add test
ntrel Apr 7, 2025
3f61d13
[unpacking] Add `-preview=tuples` CLI switch
ntrel Apr 13, 2025
7658534
[unpacking] Tweak lone identifier error message, add test
ntrel Apr 14, 2025
96dad60
[unpacking] Fix parsing malformed auto declarations starting with `(`…
ntrel Apr 14, 2025
aa3c194
Test extern/scope unpacking.
tgehr Apr 14, 2025
dd18231
[unpacking] Add test for missing identifier in unpack declaration (#9)
ntrel Apr 14, 2025
1036539
[unpacking] Test semantic errors, tweak RHS mismatch error (#10)
ntrel Apr 14, 2025
1e531e3
[unpacking] Add tests for invalid parameter storage classes (#11)
ntrel Apr 18, 2025
2085a63
[unpacking] Support `auto (a, int b) =` (#13)
ntrel Apr 25, 2025
4ae6e1f
[unpacking] Error when unpacking declaration itself has a UDA
ntrel May 15, 2025
8fce78f
Tweak unpack declaration LOC
ntrel May 15, 2025
ead2762
Fix ICE in tuple unpack declaration with moved value
MetaLang Jun 20, 2026
b0d80cf
Add changelog entry for new tuple unpacking feature
MetaLang Jun 25, 2026
ff94a51
Disallow `(T x,) = tup, i = 1;`
ntrel Jun 25, 2026
9f385a4
[spec/declaration] Unpacking declarations
ntrel Jun 24, 2026
70d8815
Change markdown examples to ddoc style `---` for spec consistency
ntrel Jun 26, 2026
fff690a
Tweak wording for pattern without storage class
ntrel Jun 27, 2026
c5ad4d7
Fix AssignExpression links
ntrel Jun 28, 2026
3d8fd03
Fix lex, type links
ntrel Jun 29, 2026
d824cff
[spec/statement] Unpacking Foreach Variables
ntrel Jun 27, 2026
1105d44
[spec/function] Unpacking Parameters
ntrel Jun 28, 2026
7588428
[changelog] Mention tuple parameters
ntrel Jun 29, 2026
04db20a
Mention unpacking in function literal templates
ntrel Jun 29, 2026
d946bf5
Fix whitespace.
tgehr Jul 6, 2026
067d047
Update `globals.h`.
tgehr Jul 6, 2026
f9a0430
Fix cxxfrontend tests.
tgehr Jul 6, 2026
61fb670
Fix Ddoc warning.
tgehr Jul 6, 2026
6bf1826
Try to appease D-Scanner.
tgehr Jul 6, 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
1 change: 1 addition & 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
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