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

[REG2.068.0] Issue 15726 - forward reference error for circular classes, RefCounted #5500

Merged
merged 4 commits into from
Mar 31, 2016

Conversation

9rnsr
Copy link
Contributor

@9rnsr 9rnsr commented Mar 4, 2016

Defer aggregate instance size finalization until semantic2 at maximum

From long ago, an instance size finalization had been done during semantic() stage, but it had had a known problem. When an aggregate 'A' tries to build its dtor or postblit, they should know each types of instance fields. But the field type analysis could recursively reach to A's sematnic() (especially with RefCounted, Variant, etc) and it had caused forward reference issues.

Against that, we've tried many ways to detect/break the problematic recursive analysis. So far it has been mostly finished to monitoring sizeok field, though it has been still fragile.

Finally I found an essential solution. During the dtor or postblit building, we don't have to finalize instance size. Instead we just need to know the complete list of instance fields at that time.

From the view, I divide instance size determination process into two stages.

The first is determineFields(), it collects all instance fields then pushes into AggregateDeclaration.fields. It's called in aggregate's semantic() and makes preparations for buildDtor() and buildPostBlit().

The second is determineSize(), it requests finalizing each type sizes of instance fields and determine the field offsets. If no declarations' semantic() depend on the instance size, determineSize() could be deferred until semantic2() stage at maximum.

Finally, a failure of determineSize() can mean an existence of unresolvable forward references (circular dependency, use of opaque struct, etc).


This PR is intentionally based on master branch, because this change is too big as a regression fix.

@dlang-bot
Copy link
Contributor

Fix Bugzilla Description
14666 [REG2.061] Bogus forward reference error
15726 [REG2.068.0] forward reference error for circular classes, RefCounted

@9rnsr 9rnsr force-pushed the fix15726 branch 6 times, most recently from 5034590 to 89213a9 Compare March 8, 2016 14:41
@9rnsr
Copy link
Contributor Author

9rnsr commented Mar 8, 2016

Ready to review.

@WalterBright
Copy link
Member

Please do refactorings as separate PRs.

@WalterBright
Copy link
Member

I've been repeating this point many times over the last few months, so I wrote this:
http://wiki.dlang.org/Starting_as_a_Contributor#Characteristics_of_a_Good_Pull_Request

@9rnsr
Copy link
Contributor Author

9rnsr commented Mar 16, 2016

Now this PR contains only core changes for issue 15726.

@9rnsr 9rnsr force-pushed the fix15726 branch 2 times, most recently from f192bd9 to bf191c7 Compare March 20, 2016 03:03
n *= (cast(TypeSArray)tv).dim.toUInteger();
tv = tv.nextOf().toBasetype();
}
if (n == 0)
Copy link
Member

Choose a reason for hiding this comment

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

We should have a small helper function for that in TypeSArray.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not bad, but today it will be used only at two places.

if (v.storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter))
return 0;
if (!v.isField() || v.sem < SemanticDone)
return 1; // unresolvable forward referernce
Copy link
Member

Choose a reason for hiding this comment

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

reference spelling

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix.

structsize = 0;
alignsize = 0;

assert(type == Type.terror);
Copy link
Member

Choose a reason for hiding this comment

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

type.ty == Terror

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix.

@CyberShadow
Copy link
Member

Please do refactorings as separate PRs.

BTW,

GitHub has now improved the commit viewer in pull requests. Now, there are prev/next buttons, so that pull requests can contain logically self-contained changes split up in commits, and reviewing can be done piecewise by looking at individual commits. This makes it easy to ignore refactoring / whitespace changes when reviewing PRs, so maybe that rule is not as necessary any more?

@9rnsr 9rnsr deleted the fix15726 branch March 31, 2016 07:50
@WalterBright
Copy link
Member

http://wiki.dlang.org/Starting_as_a_Contributor#Characteristics_of_a_Good_Pull_Request

I really don't understand the resistance to having small, one topic, focused PRs, especially when they are complex. Even this one was unnecessarily difficult because a lot of the diff lines were still refactorings unrelated to the purpose of the PR, and the github diff view still displayed them. I still wound up looking at 10 line blocks wondering "is this a refactoring, or is it substantive?"

A good PR should do one thing, for darn good reasons. It's analogous to not liking spaghetti code, and unencapsulated data structures. We certainly have plenty of regressions from PRs.

It's not like there aren't bugs in the refactorings. There is a history of them.

@CyberShadow
Copy link
Member

Well, I'm asking because I don't understand the resistance against it. In particular...

I still wound up looking at 10 line blocks wondering "is this a refactoring, or is it substantive?"

Well, that's what looking at individual commits is supposed to solve, isn't it?

A good PR should do one thing, for darn good reasons.

Could you please explain how splitting changes into logically separate PRs is different from splitting changes into logically separate commits?

It's analogous to not liking spaghetti code, and unencapsulated data structures.

I don't see how that analogy holds, because the changes are encapsulated, but in commits, not PRs.

As for the arguments for this, well, if you need to refactor something to fix something else, then the refactoring is blocking your other work, which I can understand to be frustrating - by the time your refactoring PR has been reviewed and merged, you're elsewhere, and have to back up to submit the actual fix. That's my understanding of the situation, and I'm mainly asking out of curiosity anyway.

@yebblies
Copy link
Member

I don't see how that analogy holds, because the changes are encapsulated, but in commits, not PRs.

Looking at the commits one-by-one is harder, because you still need to load the full thing into your head. Quite often it's not obvious which commits depend on which other commits, or at which points in the middle the code still works. Sometimes code is added in one commit and removed in another. With multiple PRs there is no question.

The PR author already knows all this information, and they can split into multiple PRs in much less time than it would take me to reconstruct this in my head. Especially if I'm doing it in 10 minute chunks here and there when I have time to read PRs. I assume others are in a similar situation.

As for the arguments for this, well, if you need to refactor something to fix something else, then the refactoring is blocking your other work, which I can understand to be frustrating - by the time your refactoring PR has been reviewed and merged, you're elsewhere, and have to back up to submit the actual fix. That's my understanding of the situation, and I'm mainly asking out of curiosity anyway.

I've never found that to be a significant problem in practice. Rebasing, pushing only some commits from a branch, etc are usually pretty easy.

9rnsr added a commit to 9rnsr/dmd that referenced this pull request Apr 24, 2016
In dlang#5500, issue 14666 has been properly fixed by adding mechanism of deferred semantic2 running for imported modules. Now, the workaround in `todt.c` is not necessary anymore.
9rnsr added a commit to 9rnsr/dmd that referenced this pull request Apr 24, 2016
In dlang#5500, issue 14666 has been properly fixed by adding mechanism of
deferred semantic2 running for imported modules. Now, the workaround for issue 9057 in `todt.c` is
unnecessary anymore.
9rnsr added a commit to 9rnsr/dmd that referenced this pull request Apr 24, 2016
In dlang#5500, issue 14666 has been properly fixed by adding mechanism of
deferred semantic2 running for imported modules. Now, the workaround for issue 9057 in `todt.c` is
unnecessary anymore.
9rnsr added a commit to 9rnsr/dmd that referenced this pull request Apr 24, 2016
In dlang#5500, issue 14666 has been properly fixed by adding mechanism of
deferred semantic2 running for imported modules. Now, the workaround for issue 9057 in `todt.c` is
unnecessary anymore.
@CyberShadow
Copy link
Member

This pull request introduced a regression:
https://issues.dlang.org/show_bug.cgi?id=16607

@MartinNowak
Copy link
Member

MartinNowak commented Oct 19, 2016

@MartinNowak
Copy link
Member

More specifically both regressions are caused by the 2nd commit of this PR 75b5b69.

MartinNowak added a commit to MartinNowak/dmd that referenced this pull request Oct 24, 2016
…sted

- Since dlang#5500 buildPostblit/Dtor/OpAssign are run before
  the struct size is finalized, thereby now making structs nested
  that previously were not.
- Not sure if the old behavior was an intended feature. If not
  we might want to deprecate it.
MartinNowak added a commit to MartinNowak/dmd that referenced this pull request Oct 24, 2016
…sted

- Since dlang#5500 buildPostblit/Dtor/OpAssign are run before
  the struct size is finalized, thereby now making structs nested
  that previously were not.
- Not sure if the old behavior was an intended feature. If not
  we might want to deprecate it.
UplinkCoder pushed a commit to UplinkCoder/dmd that referenced this pull request Nov 3, 2016
…sted

- Since dlang#5500 buildPostblit/Dtor/OpAssign are run before
  the struct size is finalized, thereby now making structs nested
  that previously were not.
- Not sure if the old behavior was an intended feature. If not
  we might want to deprecate it.
kinke pushed a commit to ldc-developers/dmd-testsuite that referenced this pull request Jan 27, 2017
…sted

- Since dlang/dmd#5500 buildPostblit/Dtor/OpAssign are run before
  the struct size is finalized, thereby now making structs nested
  that previously were not.
- Not sure if the old behavior was an intended feature. If not
  we might want to deprecate it.
@ibuclaw
Copy link
Member

ibuclaw commented Feb 10, 2017

And another regression where test13613.S1 puts out 4 fields, instead of 3 because there's no protection against recursive calls to determineFields. Which looks like it is fixed by #5773.

@ibuclaw
Copy link
Member

ibuclaw commented Feb 10, 2017

Specifically, this commit, which adds recursive protection. 80d1398

@CyberShadow
Copy link
Member

This pull request introduced a regression:
https://issues.dlang.org/show_bug.cgi?id=17307

@CyberShadow
Copy link
Member

This pull request introduced a regression:
https://issues.dlang.org/show_bug.cgi?id=18606

@CyberShadow
Copy link
Member

This pull request may have introduced a regression:
https://issues.dlang.org/show_bug.cgi?id=20753

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

Successfully merging this pull request may close these issues.

7 participants