Skip to content
Merged
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
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ before_script:

script:
# note: for dlang project tester, which doesn't clone automatically
- meson build && ninja -j8 -C build
- ninja -j8 -C build test -v
#
# Meson tests are disabled for now because old dependencies caused builds to fail
#- meson build && ninja -j8 -C build
#- ninja -j8 -C build test -v
- git submodule update --init --recursive
#
- cd test && ./run_tests.sh && cd ..
Expand Down
17 changes: 16 additions & 1 deletion src/dparse/ast.d
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ abstract class ASTVisitor

/** */ void visit(const AddExpression addExpression) { addExpression.accept(this); }
/** */ void visit(const AliasDeclaration aliasDeclaration) { aliasDeclaration.accept(this); }
/** */ void visit(const AliasAssign aliasAssign) { aliasAssign.accept(this); }
/** */ void visit(const AliasInitializer aliasInitializer) { aliasInitializer.accept(this); }
/** */ void visit(const AliasThisDeclaration aliasThisDeclaration) { aliasThisDeclaration.accept(this); }
/** */ void visit(const AlignAttribute alignAttribute) { alignAttribute.accept(this); }
Expand Down Expand Up @@ -487,6 +488,19 @@ final class AliasDeclaration : BaseNode
/** */ MemberFunctionAttribute[] memberFunctionAttributes;
}

///
final class AliasAssign : BaseNode
{
override void accept(ASTVisitor visitor) const
{
mixin (visitIfNotNull!(identifier, type));
}
mixin OpEquals;
/** */ Token identifier;
/** */ Type type;
/** */ string comment;
}

///
final class AliasInitializer : BaseNode
{
Expand Down Expand Up @@ -1279,7 +1293,7 @@ final class Declaration : BaseNode
private import std.variant:Algebraic;
private import std.typetuple:TypeTuple;

alias DeclarationTypes = TypeTuple!(AliasDeclaration, AliasThisDeclaration,
alias DeclarationTypes = TypeTuple!(AliasDeclaration, AliasAssign, AliasThisDeclaration,
AnonymousEnumDeclaration, AttributeDeclaration,
ClassDeclaration, ConditionalDeclaration, Constructor, DebugSpecification,
Destructor, EnumDeclaration, EponymousTemplateDeclaration,
Expand All @@ -1302,6 +1316,7 @@ final class Declaration : BaseNode
/** */ Declaration[] declarations;

mixin(generateProperty("AliasDeclaration", "aliasDeclaration"));
mixin(generateProperty("AliasAssign", "aliasAssign"));
mixin(generateProperty("AliasThisDeclaration", "aliasThisDeclaration"));
mixin(generateProperty("AnonymousEnumDeclaration", "anonymousEnumDeclaration"));
mixin(generateProperty("AttributeDeclaration", "attributeDeclaration"));
Expand Down
62 changes: 45 additions & 17 deletions src/dparse/parser.d
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,25 @@ class Parser
return attachCommentFromSemicolon(node, startIndex);
}

/**
* Parses an AliasAssign.
*
* $(GRAMMAR $(RULEDEF aliasAssign):
* $(LITERAL Identifier) $(LITERAL '=') $(RULE type)
*/
AliasAssign parseAliasAssign()
{
mixin(traceEnterAndExit!(__FUNCTION__));
auto startIndex = index;
auto node = allocator.make!AliasAssign;
node.comment = comment;
comment = null;
mixin(tokenCheck!(`node.identifier`, "identifier"));
mixin(tokenCheck!"=");
mixin(parseNodeQ!(`node.type`, `Type`));
return attachCommentFromSemicolon(node, startIndex);
}

/**
* Parses an AliasInitializer.
*
Expand Down Expand Up @@ -1809,7 +1828,7 @@ class Parser
* | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)*
* ;)
*/
ConditionalDeclaration parseConditionalDeclaration(bool strict)
ConditionalDeclaration parseConditionalDeclaration(bool strict, bool inTemplateDeclaration = false)
{
mixin(traceEnterAndExit!(__FUNCTION__));
auto startIndex = index;
Expand All @@ -1825,7 +1844,7 @@ class Parser
while (moreTokens() && !currentIs(tok!"}") && !currentIs(tok!"else"))
{
immutable c = allocator.setCheckpoint();
if (!trueDeclarations.put(parseDeclaration(strict, true)))
if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration)))
{
allocator.rollback(c);
return null;
Expand All @@ -1836,7 +1855,7 @@ class Parser
}
else
{
if (!trueDeclarations.put(parseDeclaration(strict, true)))
if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration)))
return null;
node.trueStyle = DeclarationListStyle.single;
}
Expand All @@ -1861,14 +1880,14 @@ class Parser
node.falseStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon;
advance();
while (moreTokens() && !currentIs(tok!"}"))
if (!falseDeclarations.put(parseDeclaration(strict, true)))
if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration)))
return null;
if (brace)
mixin(tokenCheck!"}");
}
else
{
if (!falseDeclarations.put(parseDeclaration(strict, true)))
if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration)))
return null;
node.falseStyle = DeclarationListStyle.single;
}
Expand Down Expand Up @@ -2063,13 +2082,15 @@ class Parser
* Params:
* strict = if true, do not return partial AST nodes on errors.
* mustBeDeclaration = do not parse as a declaration if it could be parsed as a function call
* inTemplateDeclaration = if this function is called from a templated context
*
* $(GRAMMAR $(RULEDEF declaration):
* $(RULE attribute)* $(RULE declaration2)
* | $(RULE attribute)+ $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')
* ;
* $(RULEDEF declaration2):
* $(RULE aliasDeclaration)
* | $(RULR aliasAssign)
* | $(RULE aliasThisDeclaration)
* | $(RULE anonymousEnumDeclaration)
* | $(RULE attributeDeclaration)
Expand Down Expand Up @@ -2100,7 +2121,7 @@ class Parser
* | $(RULE versionSpecification)
* ;)
*/
Declaration parseDeclaration(bool strict = false, bool mustBeDeclaration = false)
Declaration parseDeclaration(bool strict = false, bool mustBeDeclaration = false, bool inTemplateDeclaration = false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are alias assignments only strictly allowed in templates?

Additionally you are missing this parameter in a lot of sub-calls here.

Example that would break as far as I can see:

// parseConditionalDeclaration
static if (true)
{
    A = B;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I see a static if (true) in there at release I'm gonna burn down Adam's shed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I could tell from the implementation [1], AliasAssign is allowed only inside template A(..){...} declarations.

I understand your point, however, it is not possible to deduce whether the static if is at function level or at template level during parsing. In dmd, this is checked during semantic analysis, so I assume that the solution for libdparse is to create an AliasAssignDeclaration every time we encounter an assignment that occurs in non-function contexts. ParseDeclaration seems to be called at function scope level also. Any ideas on how I could differentiate between assignments at function level and assignments non-function levels?

[1] https://github.com/dlang/dmd/pull/11846/files?file-filters%5B%5D=.d&file-filters%5B%5D=.h#diff-90e6074482976dda59fff5b12d8aa29f1af3012b5db226a827e8e304b953ec10R6774

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to nest the inTemplateDeclaration parameter into all calls that should keep it, ie. here the parseConditionalDeclaration function and all the other parse*Declaration functions you want to support.

I personally think it would be more pretty doing this with some kind of parse state struct that is passed as argument in all related functions though if this is expected to expand in the future to more edge cases.

Copy link
Contributor Author

@RazvanN7 RazvanN7 May 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this will work. Consider this case:

template X(T)
{
    alias X = int;
    static if (cond)
        X = float;            // this one is currently accepted by dmd
    void fun()
    {
         X = T;               // while this one is not because the assignment
                              // does not have the same parent as the declaration of X
    }
}

This is not going to get solved by passing the inTemplate boolean. Or maybe it could work, but that would mean inserting a primitive form of semantic analysis. Right now, libdparse issues an error when
assignments occur in non-function contexts. If we want to keep things in parser territory, we need to relax that condition and to create AliasAssignDeclarations if an assignment occurs in non-function context. Of course that will allow for code that is not an alias assignment to pass parsing, but that is fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the parseFunctionDeclaration body would reset back the boolean value that you are in a template, (implicitly by simply not passing it down as parameter or for a struct explicitly) you will want to do that with anything adding scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I'll try that out. Thanks for your assistance!

{
mixin(traceEnterAndExit!(__FUNCTION__));
auto startIndex = index;
Expand Down Expand Up @@ -2200,7 +2221,7 @@ class Parser
while (moreTokens() && !currentIs(tok!"}"))
{
auto c = allocator.setCheckpoint();
if (!declarations.put(parseDeclaration(strict)))
if (!declarations.put(parseDeclaration(strict, false, inTemplateDeclaration)))
{
allocator.rollback(c);
return null;
Expand Down Expand Up @@ -2335,11 +2356,11 @@ class Parser
else if (peekIs(tok!"~"))
mixin(parseNodeQ!(`node.staticDestructor`, `StaticDestructor`));
else if (peekIs(tok!"if"))
mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict)`);
mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`);
else if (peekIs(tok!"assert"))
mixin(parseNodeQ!(`node.staticAssertDeclaration`, `StaticAssertDeclaration`));
else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse"))
mixin(parseNodeQ!(`node.staticForeachDeclaration`, `StaticForeachDeclaration`));
mixin(nullCheck!(`node.staticForeachDeclaration = parseStaticForeachDeclaration(inTemplateDeclaration)`));
else
goto type;
break;
Expand All @@ -2359,6 +2380,13 @@ class Parser
mixin(parseNodeQ!(`node.unittest_`, `Unittest`));
break;
case tok!"identifier":
if (inTemplateDeclaration && peekIs(tok!"="))
{
mixin(parseNodeQ!(`node.aliasAssign`, `AliasAssign`));
break;
}
else
goto type;
case tok!".":
case tok!"const":
case tok!"immutable":
Expand Down Expand Up @@ -2395,7 +2423,7 @@ class Parser
break;
case tok!"version":
if (peekIs(tok!"("))
mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict)`);
mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`);
else if (peekIs(tok!"="))
mixin(parseNodeQ!(`node.versionSpecification`, `VersionSpecification`));
else
Expand All @@ -2408,7 +2436,7 @@ class Parser
if (peekIs(tok!"="))
mixin(parseNodeQ!(`node.debugSpecification`, `DebugSpecification`));
else
mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict)`);
mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`);
break;
default:
error("Declaration expected");
Expand Down Expand Up @@ -3139,12 +3167,12 @@ class Parser
* | $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}'))
* ;)
*/
StaticForeachDeclaration parseStaticForeachDeclaration()
StaticForeachDeclaration parseStaticForeachDeclaration(bool inTemplateDeclaration = false)
{
mixin(traceEnterAndExit!(__FUNCTION__));
auto startIndex = index;
mixin(tokenCheck!"static");
auto decl = parseForeach!true();
auto decl = parseForeach!true(inTemplateDeclaration);
if (decl) decl.tokens = tokens[startIndex .. index];
return decl;
}
Expand Down Expand Up @@ -3177,7 +3205,7 @@ class Parser
return parseForeach!false();
}

Foreach!declOnly parseForeach(bool declOnly = false)()
Foreach!declOnly parseForeach(bool declOnly = false)(bool inTemplateDeclaration = false)
{
mixin(traceEnterAndExit!(__FUNCTION__));
auto startIndex = index;
Expand Down Expand Up @@ -3233,7 +3261,7 @@ class Parser
{
immutable b = setBookmark();
immutable c = allocator.setCheckpoint();
if (declarations.put(parseDeclaration(true, true)))
if (declarations.put(parseDeclaration(true, true, inTemplateDeclaration)))
abandonBookmark(b);
else
{
Expand All @@ -3244,7 +3272,7 @@ class Parser
}
mixin(tokenCheck!"}");
}
else if (!declarations.put(parseDeclaration(true, true)))
else if (!declarations.put(parseDeclaration(true, true, inTemplateDeclaration)))
return null;
ownArray(node.declarations, declarations);
}
Expand Down Expand Up @@ -6684,7 +6712,7 @@ class Parser
while (moreTokens() && !currentIs(tok!"}"))
{
immutable c = allocator.setCheckpoint();
if (!declarations.put(parseDeclaration(true, true)))
if (!declarations.put(parseDeclaration(true, true, true)))
allocator.rollback(c);
}
ownArray(node.declarations, declarations);
Expand Down
105 changes: 105 additions & 0 deletions test/pass_files/alias_assign.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
template T(X)
{
alias A = int;
A = X;
struct B
{
A t;
}
}

void fun()
{
auto b = T!float.B();
}

// https://github.com/dlang/phobos/pull/8033
template EraseAll(args...)
if (args.length >= 1)
{
alias EraseAll = AliasSeq!();
static foreach (arg; args[1 .. $])
static if (!isSame!(args[0], arg))
EraseAll = AliasSeq!(EraseAll, arg);
}

// https://github.com/dlang/phobos/pull/8034
template NoDuplicates(args...)
{
alias NoDuplicates = AliasSeq!();
static foreach (arg; args)
NoDuplicates = AppendUnique!(NoDuplicates, arg);
}

// https://github.com/dlang/phobos/pull/8036
template ReplaceAll(args...)
{
alias ReplaceAll = AliasSeq!();
static foreach (arg; args[2 .. $])
{
static if (isSame!(args[0], arg))
ReplaceAll = AliasSeq!(ReplaceAll, args[1]);
else
ReplaceAll = AliasSeq!(ReplaceAll, arg);
}
}

// https://github.com/dlang/phobos/pull/8037
template Reverse(args...)
{
alias Reverse = AliasSeq!();
static foreach_reverse (arg; args)
Reverse = AliasSeq!(Reverse, arg);
}

// https://github.com/dlang/phobos/pull/8038
template MostDerived(T, TList...)
{
import std.traits : Select;
alias MostDerived = T;
static foreach (U; TList)
MostDerived = Select!(is(U : MostDerived), U, MostDerived);
}

// https://github.com/dlang/phobos/pull/8044
template Repeat(size_t n, items...)
{
static if (n == 0)
{
alias Repeat = AliasSeq!();
}
else
{
alias Repeat = items;
enum log2n =
{
uint result = 0;
auto x = n;
while (x >>= 1)
++result;
return result;
}();
static foreach (i; 0 .. log2n)
{
Repeat = AliasSeq!(Repeat, Repeat);
}
Repeat = AliasSeq!(Repeat, Repeat!(n - (1u << log2n), items));
}
}

// https://github.com/dlang/phobos/pull/8047
template Stride(int stepSize, Args...)
if (stepSize != 0)
{
alias Stride = AliasSeq!();
static if (stepSize > 0)
{
static foreach (i; 0 .. (Args.length + stepSize - 1) / stepSize)
Stride = AliasSeq!(Stride, Args[i * stepSize]);
}
else
{
static foreach (i; 0 .. (Args.length - stepSize - 1) / -stepSize)
Stride = AliasSeq!(Stride, Args[$ - 1 + i * stepSize]);
}
}