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

Fix duplicate function detection for overloads introduced by aliases #12053

Merged
merged 1 commit into from
Jan 31, 2021

Conversation

MoonlightSentinel
Copy link
Contributor

@MoonlightSentinel MoonlightSentinel commented Dec 26, 2020

The previous implementation compared small parts of the function mangling
to detect duplicate functions. This approach had several flaws manifesting
in the referenced issue as well as other false positives for (static)
members, `alias`ing into different scopes, ... .

Issue 21505 was caused by the assumption that all functions visited by
`overloadApply` have the same name. This doesn't hold for overloads
introduced by an `alias`.

A reliable mangling-based implementation would need to compare most of
the mangled name while also omitting severel aspects e.g. the return type
to detect `int foo()` and `void foo()`.

Hence the current implementation was replaced by actually comparing the
`FuncDeclaration`s because it allows for more fine grained checks (and
also should save some memory allocations).

Added the intial report as a bugzilla reference

@MoonlightSentinel MoonlightSentinel changed the base branch from master to stable December 26, 2020 15:10
@MoonlightSentinel MoonlightSentinel force-pushed the alias-overload branch 3 times, most recently from c537f23 to 34d99fb Compare December 26, 2020 16:31
@MoonlightSentinel MoonlightSentinel marked this pull request as ready for review December 26, 2020 17:23
@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @MoonlightSentinel! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Auto-close Bugzilla Severity Description
21505 regression Function alias reported as conflicting function

Testing this PR locally

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

dub run digger -- build "stable + dmd#12053"

test/fail_compilation/fail2789.d Show resolved Hide resolved
@@ -53,7 +47,7 @@ void f4() {}
void f4() {} // conflict

void f5() @safe {}
void f5() @system {} // conflict
void f5() @system {} // no conflict because of attribute based overloading in in extern(D)
Copy link
Member

Choose a reason for hiding this comment

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

Yes, extern(D) preserves the mangling, but does overload resolution work well, or the user has to resort to hackery (e.g. externDFunc to traits)? If the answer is no, I think it's not a bad idea to deprecate such form of likely accidental overloading.

Copy link
Contributor Author

@MoonlightSentinel MoonlightSentinel Dec 28, 2020

Choose a reason for hiding this comment

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

Using __traits(getOverloads, ...) should also work IIRC.
But thats out of scope for this PR.

EDIT: It does work e.g. for opApply IIRC

src/dmd/dmangle.d Outdated Show resolved Hide resolved
@RazvanN7
Copy link
Contributor

RazvanN7 commented Jan 6, 2021

An alternative fix would be to disallow inserting an overload in an overload list via alias this if the inserted function might conflict with any of the other overloads. Inserting a conflicting overload can never be useful.

@MoonlightSentinel MoonlightSentinel marked this pull request as draft January 21, 2021 16:47
@MoonlightSentinel MoonlightSentinel force-pushed the alias-overload branch 2 times, most recently from da8b9ae to 15fe4b7 Compare January 25, 2021 17:32
@MoonlightSentinel
Copy link
Contributor Author

MoonlightSentinel commented Jan 25, 2021

Switchted to comparing the FuncDeclarations instead of comparing the mangling because the latter doesn't work for several cases aside from the initial bug report (unless it compares the entire mangling)

Some examples:

  • the bug report
  • static member funtions
  • alias introducing overloads into different scopes (e.g. free function inserted into an aggregate)

EDIT: Will squash & adapt the commit message once it is ready.

@MoonlightSentinel MoonlightSentinel marked this pull request as ready for review January 25, 2021 17:54
@MoonlightSentinel
Copy link
Contributor Author

@RazvanN7 You can still call the function using __traits(getOverloads):

extern (C) int puts(scope const char*) @trusted @nogc;

void func() @safe { puts("@safe"); }
void func() @nogc { puts("@nogc"); }

void main() @nogc
{
    __traits(getOverloads, M, "func")[1](); // Writes "@nogc"
}

But I can also defer those changes into a latter PR and retain the current behaviour for now.

@WalterBright
Copy link
Member

With this behavior change, how does it affect the Spec?

@MoonlightSentinel
Copy link
Contributor Author

The spec doesn't mention conflicting functions IIRC. That's why the proposed implementation checks only for functions that cause issues in the backend (function merging for dmd/errors for ldc/gdc).

@RazvanN7 RazvanN7 added the Merge:72h no objection -> merge The PR will be merged if there are no objections raised. label Jan 28, 2021
The previous implementation compared small parts of the function mangling
to detect duplicate functions. This approach had several flaws manifesting
in the referenced issue as well as other false positives for (static)
members, `alias`ing into different scopes, ... .

Issue 21505 was caused by the assumption that all functions visited by
`overloadApply` have the same name. This doesn't hold for overloads
introduced by an `alias`.

A reliable mangling-based implementation would need to compare most of
the mangled name while also omitting severel aspects e.g. the return type
to detect `int foo()` and `void foo()`.

Hence the current implementation was replaced by actually comparing the
`FuncDeclaration`s because it allows for more fine grained checks (and
also should save some memory allocations).
@MoonlightSentinel
Copy link
Contributor Author

Rebased & squashed the commits

@RazvanN7 RazvanN7 merged commit 41fa513 into dlang:stable Jan 31, 2021
@MartinNowak MartinNowak mentioned this pull request Feb 12, 2021
@dkorpel
Copy link
Contributor

dkorpel commented Feb 28, 2021

I don't know why, but this change introduced a lot of errors in the form of address of variable x assigned to y with longer lifetime in my code. I'm working on a reduced test case.

@MoonlightSentinel MoonlightSentinel deleted the alias-overload branch February 28, 2021 12:29
@dkorpel
Copy link
Contributor

dkorpel commented Feb 28, 2021

@safe:

struct I {int* x;}
pure I gcd(I, I) {return I();}

auto foo(I func) {
	if (R n = func)
		R r = n.foo!()(n); // Error: address of variable n assigned to r with longer lifetime`
}

struct R {
@safe:
	I d;
	this(I) {}
	bool opCast() {return true;}

	R foo()(R) out {check();} do {
		gcd(d, d);
		return this;
	}

	bool check() {return true;}
}

When compiled with -dip1000, it says Error: address of variable n assigned to r with longer lifetime.
When reverting this commit, it doesn't happen. I don't know how this PR changed this, but it seems to be a side-effect: small changes to the snippet make or break the new / old compiler version. I don't even know if this is accepts-invalid or rejects-valid.

@MoonlightSentinel
Copy link
Contributor Author

The DIP1000 implementation is very brittle, so I guess this error is probably depending on the order of semantic analysis.
I guess this PR affects your code because the parameter checks causes an earlier expansion & analysis of the function parameters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Merge:72h no objection -> merge The PR will be merged if there are no objections raised. Severity:Bug Fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants