Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions compiler/include/dmd/aggregate.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "dsymbol.h"
#include "objc.h"

class TypeSumType;

class AliasThis;
class Identifier;
class Type;
Expand Down Expand Up @@ -162,6 +164,7 @@ class StructDeclaration : public AggregateDeclaration
// ABI-specific type(s) if the struct can be passed in registers
TypeTuple *argTypes;

TypeSumType *sumtype; // if non-null, this struct is the lowered form of a __sumtype
structalign_t alignment; // alignment applied outside of the struct
ThreeState ispod; // if struct is POD
private:
Expand Down
25 changes: 25 additions & 0 deletions compiler/include/dmd/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ class DotVarExp final : public UnaExp
public:
Declaration *var;
d_bool hasOverloads;
d_bool compilerOverlappedAccess;

void accept(Visitor *v) override { v->visit(this); }
};
Expand Down Expand Up @@ -1256,6 +1257,30 @@ class GenericExp final : Expression

/****************************************************************/

struct SumTypeMatchArmInfo
{
VarDeclaration *vd;
Expression *guard;
int variantIndex;
int originalIndex;
};

class MatchExp final : Expression
{
public:
Expression *arg;
Array<SumTypeMatchArmInfo> *armInfos;
Type *resultType;
StructDeclaration *loweredStruct;
TypeSumType *sumtypeType;

MatchExp *syntaxCopy() override;

void accept(Visitor *v) override { v->visit(this); }
};

/****************************************************************/

class DefaultInitExp : public Expression
{
public:
Expand Down
27 changes: 27 additions & 0 deletions compiler/include/dmd/mtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ enum class TY : uint8_t
Ttraits,
Tmixin,
Tnoreturn,
Ttag,
Tsumtype,
TMAX
};

Expand Down Expand Up @@ -284,6 +286,7 @@ class Type : public ASTNode
TypeTraits *isTypeTraits();
TypeNoreturn *isTypeNoreturn();
TypeTag *isTypeTag();
TypeSumType *isTypeSumType();

void accept(Visitor *v) override { v->visit(this); }
};
Expand Down Expand Up @@ -713,6 +716,30 @@ class TypeTag final : public Type

/**************************************************************/

struct SumTypeVariantInfo
{
Type *type;
Identifier *name;
Expressions *udas;
const char *comment;
};

typedef Array<SumTypeVariantInfo> SumTypeVariantInfos;

class TypeSumType final : public Type
{
public:
SumTypeVariantInfos *variantInfos;
StructDeclaration *loweredStruct;
size_t defaultVariantIdx;

TypeSumType *syntaxCopy() override;

void accept(Visitor *v) override { v->visit(this); }
};

/**************************************************************/

namespace dmd
{
// If the type is a class or struct, returns the symbol for it, else null.
Expand Down
6 changes: 6 additions & 0 deletions compiler/include/dmd/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ enum class TOK : unsigned char
whitespace,
rvalue,

sumtype_,

// C only keywords
inline_,
register_,
Expand Down Expand Up @@ -430,6 +432,10 @@ enum class EXP : unsigned char
_Generic_,
interval,

loweredAssignExp,
rvalue,
matchExp,

MAX
};

Expand Down
4 changes: 4 additions & 0 deletions compiler/include/dmd/visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class TypeNoreturn;
class TypeTraits;
class TypeMixin;
class TypeTag;
class TypeSumType;

class Dsymbol;

Expand Down Expand Up @@ -298,6 +299,7 @@ class ClassReferenceExp;
class VoidInitExp;
class ThrownExceptionExp;
class GenericExp;
class MatchExp;

class TemplateParameter;
class TemplateTypeParameter;
Expand Down Expand Up @@ -450,6 +452,7 @@ class ParseTimeVisitor
virtual void visit(TypeTraits *t) { visit((Type *)t); }
virtual void visit(TypeMixin *t) { visit((Type *)t); }
virtual void visit(TypeTag *t) { visit((Type *)t); }
virtual void visit(TypeSumType *t) { visit((Type *)t); }

// TypeNext
virtual void visit(TypeReference *t) { visit((TypeNext *)t); }
Expand Down Expand Up @@ -499,6 +502,7 @@ class ParseTimeVisitor
virtual void visit(TupleExp *e) { visit((Expression *)e); }
virtual void visit(ThisExp *e) { visit((Expression *)e); }
virtual void visit(GenericExp *e) { visit((Expression *)e); }
virtual void visit(MatchExp *e) { visit((Expression *)e); }

// Miscellaneous
virtual void visit(VarExp *e) { visit((SymbolExp *)e); }
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/arraytypes.d
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ alias TemplateInstances = Array!(TemplateInstance);
alias Ensures = Array!(Ensure);
alias Designators = Array!(Designator);
alias DesigInits = Array!(DesigInit);
alias SumTypeVariantInfos = Array!(SumTypeVariantInfo);
alias SumTypeMatchArmInfos = Array!(SumTypeMatchArmInfo);
63 changes: 63 additions & 0 deletions compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -2702,6 +2702,7 @@ struct ASTBase
sizeTy[Tmixin] = __traits(classInstanceSize, TypeMixin);
sizeTy[Tnoreturn] = __traits(classInstanceSize, TypeNoreturn);
sizeTy[Ttag] = __traits(classInstanceSize, TypeTag);
sizeTy[Tsumtype] = __traits(classInstanceSize, TypeSumType);
return sizeTy;
}();

Expand Down Expand Up @@ -3784,6 +3785,39 @@ struct ASTBase
}
}

struct SumTypeVariantInfo
{
Type type;
Identifier name;
Expressions* udas;
const(char)* comment;
}

alias SumTypeVariantInfos = Array!(SumTypeVariantInfo);

extern (C++) final class TypeSumType : Type
{
SumTypeVariantInfos* variantInfos;
StructDeclaration loweredStruct;
size_t defaultVariantIdx;

extern (D) this(SumTypeVariantInfos* variantInfos = null)
{
super(Tsumtype);
this.variantInfos = variantInfos;
}

override TypeSumType syntaxCopy()
{
return this;
}

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

extern (C++) final class TypeReference : TypeNext
{
extern (D) this(Type t)
Expand Down Expand Up @@ -6270,6 +6304,35 @@ struct ASTBase
}
}

struct SumTypeMatchArmInfo
{
VarDeclaration vd;
Expression guard;
int variantIndex;
int originalIndex;
}

alias SumTypeMatchArmInfos = Array!(SumTypeMatchArmInfo);

extern (C++) final class MatchExp : Expression
{
Expression arg;
SumTypeMatchArmInfos* armInfos;
Type resultType;

extern (D) this(Loc loc, Expression arg, SumTypeMatchArmInfos* armInfos)
{
super(loc, EXP.matchExp, __traits(classInstanceSize, MatchExp));
this.arg = arg;
this.armInfos = armInfos;
}

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

extern (C++) final class ErrorExp : Expression
{
extern (D) this()
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/astcodegen.d
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ struct ASTCodegen
alias Tvoid = dmd.mtype.Tvoid;
alias Twchar = dmd.mtype.Twchar;
alias Tnoreturn = dmd.mtype.Tnoreturn;
alias Tsumtype = dmd.mtype.Tsumtype;

alias Timaginary32 = dmd.mtype.Timaginary32;
alias Timaginary64 = dmd.mtype.Timaginary64;
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/astenums.d
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ enum TY : ubyte
Tmixin,
Tnoreturn,
Ttag,
Tsumtype,
}
enum TMAX = TY.max + 1;

Expand Down Expand Up @@ -279,6 +280,7 @@ alias Ttraits = TY.Ttraits;
alias Tmixin = TY.Tmixin;
alias Tnoreturn = TY.Tnoreturn;
alias Ttag = TY.Ttag;
alias Tsumtype = TY.Tsumtype;

enum TFlags
{
Expand Down
89 changes: 89 additions & 0 deletions compiler/src/dmd/dcast.d
Original file line number Diff line number Diff line change
Expand Up @@ -1549,6 +1549,72 @@ MATCH implicitConvTo(Expression e, Type t)
}
}

/********************************
* If `from` is a sumtype and `to` is a (wider) sumtype that contains
* every variant of `from`, then `from` can be implicitly converted to `to`.
*
* Params:
* from = candidate source type
* to = candidate destination type
* Returns:
* MATCH.convert if `from` widens into `to`, MATCH.nomatch otherwise.
*/
private MATCH sumtypeWidenMatch(Type from, Type to)
{
TypeSumType fromSum;
if (auto ts = from.isTypeSumType())
fromSum = ts;
else if (auto tsa = from.isTypeStruct())
if (tsa.sym && tsa.sym.sumtype)
fromSum = tsa.sym.sumtype;
if (!fromSum)
return MATCH.nomatch;

TypeSumType toSum;
if (auto ts = to.isTypeSumType())
toSum = ts;
else if (auto tsa = to.isTypeStruct())
if (tsa.sym && tsa.sym.sumtype)
toSum = tsa.sym.sumtype;
if (!toSum || fromSum == toSum)
return MATCH.nomatch;

// Every source variant must be a variant of the target.
//
// This is two-pass: first look for an exact type match, then fall back to
// an implicit conversion. This is deliberate — with a single
// "equals || implicitConvTo" check, a bool source variant would match an
// int target variant (because bool implicitly converts to int), corrupting
// the active variant during widening. Preferring the exact match preserves
// which variant is active.
foreach (vi; *fromSum.variantInfos)
{
bool found = false;
foreach (vj; *toSum.variantInfos)
{
if (vi.type.equals(vj.type))
{
found = true;
break;
}
}
if (!found)
{
foreach (vj; *toSum.variantInfos)
{
if (vi.type.implicitConvTo(vj.type) != MATCH.nomatch)
{
found = true;
break;
}
}
}
if (!found)
return MATCH.nomatch;
}
return MATCH.convert;
}

/********************************
* Determine if 'from' can be implicitly converted
* to type 'to'.
Expand Down Expand Up @@ -1903,6 +1969,21 @@ MATCH implicitConvTo(Type from, Type to)
MATCH visitStruct(TypeStruct from)
{
//printf("TypeStruct::implicitConvTo(%s => %s)\n", from.toChars(), to.toChars());

// For sumtypes, the normal struct conversion handles the same-struct
// case (e.g. adding const); widening only applies to a *different*
// (wider) sumtype struct.
if (from.sym.sumtype !is null)
{
auto tos = to.isTypeStruct();
if (tos && from.sym == tos.sym)
{
MATCH m = from.implicitConvToWithoutAliasThis(to);
return m == MATCH.nomatch ? from.implicitConvToThroughAliasThis(to) : m;
}
return sumtypeWidenMatch(from.sym.sumtype, to);
}

MATCH m = from.implicitConvToWithoutAliasThis(to);
return m == MATCH.nomatch ? from.implicitConvToThroughAliasThis(to) : m;
}
Expand Down Expand Up @@ -2003,6 +2084,7 @@ MATCH implicitConvTo(Type from, Type to)
case Ttuple: return visitTuple(from.isTypeTuple());
case Tnull: return visitNull(from.isTypeNull());
case Tnoreturn: return visitNoreturn(from.isTypeNoreturn());
case Tsumtype: { const m = sumtypeWidenMatch(from, to); return m == MATCH.nomatch ? visitType(from) : m; }
}
}

Expand Down Expand Up @@ -2152,6 +2234,13 @@ Expression castTo(Expression e, Scope* sc, Type t, Type att = null)
return result;
}

// Sumtype widening: a narrower sumtype is implicitly convertible to a wider one
if (sumtypeWidenMatch(e.type, tob) != MATCH.nomatch)
{
if (auto widened = sumtypeWidenExpression(e, sc, tob))
return widened;
}

/* Make semantic error against invalid cast between concrete types.
* Assume that 'e' is never be any placeholder expressions.
* The result of these checks should be consistent with CastExp::toElem().
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dmd/dfa/fast/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,7 @@ struct ExpressionWalker
case EXP._Generic:
case EXP.interval:

case EXP.matchExp:
case EXP.rvalue:
if (dfaCommon.debugUnknownAST)
{
Expand Down
Loading
Loading