Skip to content
Open
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: 2 additions & 1 deletion compiler/include/dmd/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,9 @@ class DotIdExp final : public UnaExp
d_bool noderef; // true if the result of the expression will never be dereferenced
d_bool wantsym; // do not replace Symbol with its initializer during semantic()
d_bool arrow; // ImportC: if -> instead of .
Loc identLoc; // location of `ident` itself (may differ from `loc`, e.g. across multiple lines)

static DotIdExp *create(Loc loc, Expression *e, Identifier *ident);
static DotIdExp *create(Loc loc, Expression *e, Identifier *ident, Loc identLoc = Loc());
void accept(Visitor *v) override { v->visit(this); }
};

Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -5617,11 +5617,13 @@ struct ASTBase
extern (C++) final class DotIdExp : UnaExp
{
Identifier ident;
Loc identLoc;

extern (D) this(Loc loc, Expression e, Identifier ident)
extern (D) this(Loc loc, Expression e, Identifier ident, Loc identLoc = Loc.init)
{
super(loc, EXP.dotIdentifier, __traits(classInstanceSize, DotIdExp), e);
this.ident = ident;
this.identLoc = identLoc.isValid() ? identLoc : loc;
}

override void accept(Visitor v)
Expand Down
8 changes: 5 additions & 3 deletions compiler/src/dmd/expression.d
Original file line number Diff line number Diff line change
Expand Up @@ -2191,16 +2191,18 @@ extern (C++) final class DotIdExp : UnaExp
bool noderef; // true if the result of the expression will never be dereferenced
bool wantsym; // do not replace Symbol with its initializer during semantic()
bool arrow; // ImportC: if -> instead of .
Loc identLoc; // location of `ident` itself (may differ from `loc`, e.g. across multiple lines)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we compute this from the loc? adding additional fields to the AST consumes more memory?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Can we compute this from the loc? adding additional fields to the AST consumes more memory?

Screenshot From 2026-09-03 20-53-12 Checked - `Loc` is just a 4-byte index, so this only adds ~5 bytes per `DotIdExp` (43→48, measured). It also can't be derived from `loc` alone since the dot-to-identifier gap varies (multi-line chains, comments). #22740 took the same approach, so I think this is fine. Do you have a suggestion to reduce it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I doubt that the additional memory consumed by this is worth the mildly better resolution of the error.
Linked PR is not merged.


extern (D) this(Loc loc, Expression e, Identifier ident) @safe
extern (D) this(Loc loc, Expression e, Identifier ident, Loc identLoc = Loc.init) @safe
{
super(loc, EXP.dotIdentifier, e);
this.ident = ident;
this.identLoc = identLoc.isValid() ? identLoc : loc;
}

static DotIdExp create(Loc loc, Expression e, Identifier ident) @safe
static DotIdExp create(Loc loc, Expression e, Identifier ident, Loc identLoc = Loc.init) @safe
{
return new DotIdExp(loc, e, ident);
return new DotIdExp(loc, e, ident, identLoc);
}

override void accept(Visitor v)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -16528,7 +16528,7 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)

const flag = cast(DotExpFlag) (exp.noderef * DotExpFlag.noDeref | gag * DotExpFlag.gag);

Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag);
Expression e = dotExp(exp.e1.type, sc, exp.e1, exp.ident, flag, exp.identLoc);
if (e)
{
e = e.expressionSemantic(sc);
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -9371,6 +9371,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
if (token.value == TOK.identifier)
{
Identifier id = token.ident;
const identLoc = token.loc;

nextToken();
if (token.value == TOK.not && peekNext() != TOK.is_ && peekNext() != TOK.in_)
Expand All @@ -9379,7 +9380,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
e = new AST.DotTemplateInstanceExp(loc, e, id, tiargs);
}
else
e = new AST.DotIdExp(loc, e, id);
e = new AST.DotIdExp(loc, e, id, identLoc);
continue;
}
if (token.value == TOK.new_)
Expand Down
10 changes: 7 additions & 3 deletions compiler/src/dmd/typesem.d
Original file line number Diff line number Diff line change
Expand Up @@ -6190,11 +6190,14 @@ void resolve(Type mt, Loc loc, Scope* sc, out Expression pe, out Type pt, out Ds
* e = expression to convert
* ident = identifier being used
* flag = DotExpFlag bit flags
* identLoc = location of `ident` itself, used for accurate error reporting
* when it differs from `e.loc` (e.g. across multiple lines);
* defaults to `e.loc` when not specified
*
* Returns:
* resulting expression with e.ident resolved
*/
Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag)
Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag flag, Loc identLoc = Loc.init)
{
enum LOGDOTEXP = false;
if (LOGDOTEXP)
Expand Down Expand Up @@ -7045,11 +7048,12 @@ Expression dotExp(Type mt, Scope* sc, Expression e, Identifier ident, DotExpFlag
Expression res = mt.sym.getMemtype(Loc.initial).dotExp(sc, e, ident, DotExpFlag.gag);
if (!(flag & 1) && !res)
{
const errLoc = identLoc.isValid() ? identLoc : e.loc;
if (auto ns = mt.sym.search_correct(ident))
eSink.error(e.loc, "no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toErrMsg(), mt.toErrMsg(), mt.toErrMsg(),
eSink.error(errLoc, "no property `%s` for type `%s`. Did you mean `%s.%s` ?", ident.toErrMsg(), mt.toErrMsg(), mt.toErrMsg(),
ns.toErrMsg());
else
eSink.error(e.loc, "no property `%s` for type `%s`", ident.toErrMsg(),
eSink.error(errLoc, "no property `%s` for type `%s`", ident.toErrMsg(),
mt.toErrMsg());

eSink.errorSupplemental(mt.sym.loc, "%s `%s` defined here",
Expand Down
17 changes: 17 additions & 0 deletions compiler/test/fail_compilation/diag21284.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
TEST_OUTPUT:
---
fail_compilation/diag21284.d(16): Error: no property `three` for type `E`
fail_compilation/diag21284.d(9): enum `E` defined here
---
*/
// https://issues.dlang.org/show_bug.cgi?id=21284 (github issue)
enum E { one, two }

void test21284()
{
auto x =
E
.
three;
}
Loading