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

Privatize constructors of tuple structs with private fields #38932

Merged
merged 5 commits into from
Feb 2, 2017

Conversation

petrochenkov
Copy link
Contributor

@petrochenkov petrochenkov commented Jan 8, 2017

This PR implements the strictest version of such "privatization" - it just sets visibilities for struct constructors, this affects everything including imports.

visibility(struct_ctor) = min(visibility(struct), visibility(field_1), ..., visibility(field_N))

Needs crater run before proceeding.

Resolves rust-lang/rfcs#902

r? @nikomatsakis

@nikomatsakis
Copy link
Contributor

Kicked off a crater run.

@petrochenkov
Copy link
Contributor Author

petrochenkov commented Jan 10, 2017

There are 3 distinct regressions:
rotor-stream-0.6.2, radiant-rs-0.1.2 and syntex_syntax-0.54.0.
Other "root" regressions actually depend on older versions of syntex_syntax.

All the regressions can be minimized like this:

mod m {
    // Tuple struct with a private field.
    // Type S is pub(pub).
    // The field S::0 is pub(m).
    // Constructor S is min(pub(pub), pub(m)) -> pub(m).
    pub struct S(u8);
    
    fn f() {
        // Try to use S from the root module in value namespace.
        // No success, ::S exists only in type namespace.
        // How to fix: use S from this module instead of ::S from the root module.
        ::S;
    }
}

// This imports S only in type namespace, value S is too private.
// This is expected filtering behavior of imports described in RFC 1560.
use m::S;

fn main() {}

The semantics visibility(struct_ctor) = min(visibility(struct), visibility(field_1), ..., visibility(field_N)) is what I'd prefer to go for instead of just prohibiting private ctors in patterns. I'm interested in what @nrc and @jseyfried think about this.

Warning period may be non-trivial, but I'll try to look what inaccessible_extern_crate does and repeat it.

@nikomatsakis
Copy link
Contributor

@petrochenkov nice analysis, interesting example. I too am curious what @jseyfried and @nrc think =)

cc @rust-lang/lang -- this has to do with the visibility of struct constructors, and the (somewhat odd, but at one time legal) pattern that breaks if you change it. Basically you could pub use the value namespace from a place that isn't allowed to use it, but only use from inside another module where it is legal to use it. This seems surprising.

@jseyfried
Copy link
Contributor

jseyfried commented Jan 11, 2017

I'm in favor of this solution -- I think it's the cleanest way to guarantee that e.g. pub struct S(u32); is indistinguishable from pub struct S { x: u32 } outside S's module.

To implement a warning cycle, I would

  • not touch import resolution; I believe this PR is highly unlikely to break import resolution in practice
  • after import resolution, warn iff
    • we can't resolve a value S in a given scope,
    • the type S in that scope resolves to a tuple struct, and
    • the tuple struct's constructor is visible.

@nrc
Copy link
Member

nrc commented Jan 11, 2017

I'm also in favour of this, seems like the sensible approach.

@nikomatsakis
Copy link
Contributor

@petrochenkov -- it seems like there is a general 👍 to this approach. Are you pursuing code for a warning period? @jseyfried has some suggestions here.

@petrochenkov
Copy link
Contributor Author

Yes, I'll update the PR in a few days.

@petrochenkov
Copy link
Contributor Author

Updated with a compatibility lint (following the strategy from #38932 (comment)).
I've made the lint deny-by-default because it's really hacky and not entirely precise, and the number of regressions is small.

@bors
Copy link
Contributor

bors commented Jan 21, 2017

☔ The latest upstream changes (presumably #39199) made this pull request unmergeable. Please resolve the merge conflicts.

@petrochenkov
Copy link
Contributor Author

Rebased.

@bors
Copy link
Contributor

bors commented Jan 22, 2017

☔ The latest upstream changes (presumably #39060) made this pull request unmergeable. Please resolve the merge conflicts.

Copy link
Contributor

@jseyfried jseyfried left a comment

Choose a reason for hiding this comment

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

r=me modulo comments


fn main() {
m::S; //~ ERROR tuple struct `S` is private
S; //~ ERROR expected value, found struct `S`
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we inform the user here that the tuple struct constructor S is private?
We could use the same condition as the warning cycle, except that we would report if the constructor were not visible.

Copy link
Contributor Author

@petrochenkov petrochenkov Jan 23, 2017

Choose a reason for hiding this comment

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

Yeah, that would be useful, but I hoped to get rid of legacy_ctor_visibilities together with the compatibility lint.
What I don't like is that this table is filled on all successful runs, but is actually needed very rarely.
Is there any way to map struct's DefId into (ctor's DefId + ctor's ty::Visibility) on demand without building tables in advance?
It'll also require extending metadata reading interface to obtain the same mapping for other crates. I really dislike adding code through the compiler that is necessary only for reporting a single special note, but well...

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with @jseyfried that the warning seems nice

Copy link
Contributor

@jseyfried jseyfried Jan 27, 2017

Choose a reason for hiding this comment

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

@petrochenkov

Is there any way to map struct's DefId into (ctor's DefId + ctor's ty::Visibility) on demand without building tables in advance?

We could simplify a little by mapping the struct's DefId to the ctor's &'a NameBinding<'a>, but we'd still be building an extra table.

To avoid that, we could add a variant

NameBindingKind::TupleStruct(Def, &'a NameBinding<'a> /* ctor */)

and use that for tuple struct type bindings instead of NameBindingKind::Def.
NameBindingKind is well encapsulated, so that shouldn't be too big of a change -- still might not be worth it though.

@@ -224,6 +224,12 @@ declare_lint! {
}

declare_lint! {
pub LEGACY_CONSTRUCTOR_VISIBILITY,
Deny,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to make this deny-by-default, given that there are three breaking crates in the wild?
While the warning cycle is a bit hacky, I think the risk of "false positives" is very low.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

3 is small, no?
I preferred deny-by-default mostly to avoid successfully compiling more illegal code by default.

Copy link
Contributor

Choose a reason for hiding this comment

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

The policy is usually warn-by-default, then deny-by-default, no? But we can make an exception, though we should make an RFC bot request about it. Either way, we should contact the authors of the crates in question and/or submit PRs.

@nikomatsakis nikomatsakis added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jan 23, 2017
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Jan 23, 2017

@rfcbot fcp merge

This is a fix to a privacy oversight that allows people to observe whether a tuple struct with a private field was defined as a tuple struct or not, something which was not intended to be public. The problem is that people can do let Foo(_) = ... only if the struct is defined as a tuple struct. The fix is to consider this illegal if the struct has only private fields.

The question is whether to make this a WARN-by-default or DENY-by-default lint. The usual policy would be warn-by-default. There are currently three root crates breaking in the wild. There are a lot of affected crates out there (285?) but I believe that most of those will be unaffected in short term because of cap-lints, though we will want to ensure they upgrade at some point.

  • rotor-stream-0.6.2 (cc @tailhook)
  • radiant-rs-0.1.2 (cc @sinesc)
  • syntex_syntax-0.54.0 (any need for a cc here?)

Given the small number of affected crates, I think starting with deny-by-default is reasonable (in particular, since this can still be overridden by cap-lints, and so most crates should be unaffected until there is time to fix the problem). Hence the FCP "merge" request. But I'd like to hear others' opinions (including the authors of the affected crates).

cc @rust-lang/compiler @rust-lang/core

@rfcbot
Copy link

rfcbot commented Jan 23, 2017

Team member @nikomatsakis has proposed to merge this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@aturon
Copy link
Member

aturon commented Jan 23, 2017

Agreed that deny-by-default seems OK here. Glad to see this getting fixed!

@sinesc
Copy link

sinesc commented Jan 24, 2017

Regarding radiant, it looks like I just forgot a "super" here and it happens to work anyway because the root pub uses the struct as well. (?)
PR makes sense to me, assuming my understanding is correct :-)

@rfcbot
Copy link

rfcbot commented Jan 25, 2017

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot added the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Jan 25, 2017
@nikomatsakis
Copy link
Contributor

r? @jseyfried

The code looks reasonable to me, but I think @jseyfried is better suited to really review this.

@jseyfried
Copy link
Contributor

@petrochenkov r=me with the diagnostics improvement.

@petrochenkov
Copy link
Contributor Author

Updated with better diagnostics.
I'm not sending this to bors, the FCP still continues. (for how long?)

@@ -11,6 +11,7 @@
fn main() {
let Box(a) = loop { };
//~^ ERROR expected tuple struct/variant, found struct `Box`
//~| ERROR expected tuple struct/variant, found struct `Box`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a compiletest bug. If one error has two labels, compiletest will require two "expected errors".

@aturon
Copy link
Member

aturon commented Jan 31, 2017

@petrochenkov

I'm not sending this to bors, the FCP still continues. (for how long?)

We're abusing RFC bot here (someday we want a mode just for getting team approval of a PR). There's not actually an FCP here; you can merge at any time.

@petrochenkov
Copy link
Contributor Author

Excellent.
@bors r=jseyfried

@bors
Copy link
Contributor

bors commented Jan 31, 2017

📌 Commit d38a8ad has been approved by jseyfried

@bors
Copy link
Contributor

bors commented Feb 1, 2017

⌛ Testing commit d38a8ad with merge 558ea08...

@bors
Copy link
Contributor

bors commented Feb 1, 2017

💔 Test failed - status-appveyor

@petrochenkov
Copy link
Contributor Author

@bors retry
(curl failure)

@bors
Copy link
Contributor

bors commented Feb 1, 2017

⌛ Testing commit d38a8ad with merge 8060de2...

@bors
Copy link
Contributor

bors commented Feb 1, 2017

💔 Test failed - status-appveyor

@petrochenkov
Copy link
Contributor Author

@bors retry
(the same curl failure)

@bors
Copy link
Contributor

bors commented Feb 1, 2017

⌛ Testing commit d38a8ad with merge 5dc365f...

@bors
Copy link
Contributor

bors commented Feb 1, 2017

💔 Test failed - status-travis

@petrochenkov
Copy link
Contributor Author

That curl issue again.
Looks like something persistent.

@alexcrichton
Copy link
Member

alexcrichton commented Feb 1, 2017 via email

@bors
Copy link
Contributor

bors commented Feb 2, 2017

⌛ Testing commit d38a8ad with merge 43e169e...

@bors
Copy link
Contributor

bors commented Feb 2, 2017

💔 Test failed - status-travis

@alexcrichton
Copy link
Member

alexcrichton commented Feb 2, 2017 via email

@bors
Copy link
Contributor

bors commented Feb 2, 2017

⌛ Testing commit d38a8ad with merge 1b6b20a...

bors added a commit that referenced this pull request Feb 2, 2017
Privatize constructors of tuple structs with private fields

This PR implements the strictest version of such "privatization" - it just sets visibilities for struct constructors, this affects everything including imports.
```
visibility(struct_ctor) = min(visibility(struct), visibility(field_1), ..., visibility(field_N))
```
Needs crater run before proceeding.

Resolves rust-lang/rfcs#902

r? @nikomatsakis
@bors
Copy link
Contributor

bors commented Feb 2, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: jseyfried
Pushing 1b6b20a to master...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants