Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor mangle-only extern(C++, namespace) into an AttribDeclaration #10021

Merged
merged 1 commit into from
Jun 14, 2019

Conversation

Geod24
Copy link
Member

@Geod24 Geod24 commented Jun 12, 2019

Originally, `extern(C++, ident)` was implemented as introducing a scope,
importing C++ semantics into D, and was made a `ScopeDsymbol`.
Introducing a scope turned out to be a behavior that was problematic,
and users of `extern(C++)` just wanted to use D semantics and have the correct mangling.

Later on, `extern(C++, "namespace")` was implemented, which removed the introduction of a scope.
However, it was implemented within the existing `Nspace`,
basically hacking around the fact that `ScopeDsymbol` introduces a scope
and injecting the symbols in the parent scope.

This commit implements a new AST node, `CPPNamespaceDeclaration`, which is an `AttributeDeclaration`.
`AttributeDeclaration` are the way storage classes (e.g. `extern(C)` and `extern(C++, class|struct)` are implemented,
as well as declarations that apply to a scope without introducing one, such as `DeprecatedDeclaration`.

Since mangling is part of the function's external interface,
CTFE on the message is run during the first semantic pass.
Since `_scope` is set earlier than `semantic` is done on the attribute,
the full `CPPNamespaceDeclaration` needs to be stored in the `Scope`,
not just the resolved `Identifiers`.

Moreover, a few tweaks are required to `cppmangle` to make it work with the new AST structure.
Once the scoped version of `extern(C++, namespace)` is deprecated, the code can be greatly simplified.

Over the past two weeks I tried two things:

After much trial and error, I came up with the explanation mentioned above.
This will fail the CI for now as I haven't tried it on Windows yet.
I can split a few changes in separate PRs, but the bulk of it is indissociable.

CC @TurkeyMan

@Geod24 Geod24 requested review from ibuclaw and RazvanN7 as code owners June 12, 2019 05:32
@dlang-bot
Copy link
Contributor

Thanks for your pull request, @Geod24!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub fetch digger
dub run digger -- build "master + dmd#10021"

@Geod24 Geod24 force-pushed the refactor-extern-cpp branch 12 times, most recently from 93cd57c to 28ab07a Compare June 12, 2019 23:54
@Geod24 Geod24 force-pushed the refactor-extern-cpp branch 3 times, most recently from 1e6697b to 40ee34c Compare June 13, 2019 12:10
@thewilsonator
Copy link
Contributor

Can you split the first three commits as a separate PR please.

@Geod24
Copy link
Member Author

Geod24 commented Jun 13, 2019

Can you split the first three commits as a separate PR please.

Sure thing: #10032

@Geod24 Geod24 force-pushed the refactor-extern-cpp branch from 375324a to 3ff20df Compare June 13, 2019 17:35
@Geod24
Copy link
Member Author

Geod24 commented Jun 13, 2019

Since this pass the full extern(C++) test-suite (as #10031 converts all the extern(C++, ns) into extern(C++, "ns") I think this is ready.

Originally, `extern(C++, ident)` was implemented as introducing a scope,
importing C++ semantics into D, and was made a `ScopeDsymbol`.
Introducing a scope turned out to be a behavior that was problematic,
and users of `extern(C++)` just wanted to use D semantics and have the correct mangling.

Later on, `extern(C++, "namespace")` was implemented, which removed the introduction of a scope.
However, it was implemented within the existing `Nspace`,
basically hacking around the fact that `ScopeDsymbol` introduces a scope
and injecting the symbols in the parent scope.

This commit implements a new AST node, `CPPNamespaceDeclaration`, which is an `AttributeDeclaration`.
`AttributeDeclaration` are the way storage classes (e.g. `extern(C)` and `extern(C++, class|struct)` are implemented,
as well as declarations that apply to a scope without introducing one, such as `DeprecatedDeclaration`.

Since mangling is part of the function's external interface,
CTFE on the message is run during the first semantic pass.
Since `_scope` is set earlier than `semantic` is done on the attribute,
the full `CPPNamespaceDeclaration` needs to be stored in the `Scope`,
not just the resolved `Identifiers`.

Moreover, a few tweaks are required to `cppmangle` to make it work with the new AST structure.
Once the scoped version of `extern(C++, namespace)` is deprecated, the code can be greatly simplified.
@Geod24 Geod24 force-pushed the refactor-extern-cpp branch from 3ff20df to fd8a824 Compare June 14, 2019 01:31
@Geod24
Copy link
Member Author

Geod24 commented Jun 14, 2019

Rebased on master since #10032 got merged

@dlang-bot dlang-bot merged commit 140937d into dlang:master Jun 14, 2019
@Geod24 Geod24 deleted the refactor-extern-cpp branch June 14, 2019 19:09
@WalterBright
Copy link
Member

Why is this called "refactoring" when @SSoulaimane claims it introduces a scope? Where does it say that? Where's the documentation?

@SSoulaimane
Copy link
Member

Why is this called "refactoring" when @SSoulaimane claims it introduces a scope? Where does it say that? Where's the documentation?

Sorry, I meant this fixed the 'introduce a scope then discard it" i.e. no scope anymore. It's just a AttribDeclaration.

@WalterBright
Copy link
Member

I surely do not understand how the code is better with this refactoring.

@Geod24
Copy link
Member Author

Geod24 commented Jun 15, 2019

#10037 (comment)

buf.writestring("N");
if (!this.substitute(sym2))
sym2.accept(this);
dg();
Copy link
Member

@SSoulaimane SSoulaimane Jun 15, 2019

Choose a reason for hiding this comment

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

@Geod24 duplicated call to dg(). and missing terminator "E" for "N".
It seems this branch isn't covered in the tests.

extern(C++, A) struct S(T){}
extern(C++) void fun(int i)(S!int);
pragma(msg, fun!0.mangleof);

Prints _Z3funILi0EEvN1AIiE1SIiE. should be _Z3funILi0EEvN1A1SIiEE.

sym2.accept(this);
dg();
}
if (sym1 !is null)
Copy link
Member

Choose a reason for hiding this comment

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

redundant check. sym1 in never null here.

const sident = se.toStringz();
if (!sident.length || !Identifier.isValidIdentifier(sident))
{
ns.exp.error("expected valid identifer for C++ namespace but got `%.*s`",
Copy link
Member

Choose a reason for hiding this comment

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

Wasn't the motive of this syntax that D identifiers don't exactly match C++? 15512.

Copy link
Contributor

@TurkeyMan TurkeyMan Jun 15, 2019

Choose a reason for hiding this comment

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

Yes, it's one of the motives, and very important.

Copy link
Member

Choose a reason for hiding this comment

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

Then checking the validity here defeats the point.

Copy link
Member Author

Choose a reason for hiding this comment

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

This code was adapted from what's existing. I also had a "Wait, what?" moment until I checked the code for isValidIdentifier. It does not check if the identifier is one of the reserved ones.

dlang-bot added a commit that referenced this pull request Jun 16, 2019
cppmangle: Follow up on #10021
merged-on-behalf-of: Nicholas Wilson <thewilsonator@users.noreply.github.com>

extern(D) override const(char)[] toString() const
{
return "extern (C++, `namespace`)";
Copy link
Member

Choose a reason for hiding this comment

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

Should include the identifier or expression instead of "namespace".

Copy link
Member Author

Choose a reason for hiding this comment

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

Nspace doesn't do that (and it allocates)
I was considering doing a refactor where toString takes a sink, but it has been put off so far.

s.namespace, () {
this.writeIdentifier(ti.tempdecl.toAlias().ident);
template_args(ti);
});
Copy link
Member

Choose a reason for hiding this comment

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

source_name isn't supposed to insert N..E but writeNamespace does.

extern(C++, "a", "b")
struct S
{
    void func();
}
pragma(msg, S.func.mangleof);

This is a regression. It prints _ZNN1a1b1SE4funcEv it used to be _ZN1a1b1S4funcEv.

return null;
return parent.pastMixinAndNspace();
}
alias pastMixinAndNspace = pastMixin;
Copy link
Member

Choose a reason for hiding this comment

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

This caused a regression (C++ headers weren't updated).

@@ -1187,6 +1180,7 @@ extern (C++) class Dsymbol : ASTNode
inout(SymbolDeclaration) isSymbolDeclaration() inout { return null; }
inout(AttribDeclaration) isAttribDeclaration() inout { return null; }
inout(AnonDeclaration) isAnonDeclaration() inout { return null; }
inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return null; }
Copy link
Member

Choose a reason for hiding this comment

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

And another regression...

@Geod24
Copy link
Member Author

Geod24 commented Jun 29, 2019

This also fixed https://issues.dlang.org/show_bug.cgi?id=19339

@RazvanN7
Copy link
Contributor

RazvanN7 commented Jun 4, 2024

This seems to have caused a regression: https://issues.dlang.org/show_bug.cgi?id=24547

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants