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

syntax: enable attributes and cfg on struct fields #38814

Merged
merged 1 commit into from
Jan 12, 2017

Conversation

Ralith
Copy link
Contributor

@Ralith Ralith commented Jan 4, 2017

This enables conditional compilation of field initializers in a struct literal, simplifying construction of structs whose fields are themselves conditionally present. For example, the intializer for the constant in the following becomes legal, and has the intuitive effect:

struct Foo {
    #[cfg(unix)]
    bar: (),
}

const FOO: Foo = Foo {
    #[cfg(unix)]
    bar: (),
};

It's not clear to me whether this calls for the full RFC process, but the implementation was simple enough that I figured I'd begin the conversation with code.

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @nrc (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@nrc
Copy link
Member

nrc commented Jan 4, 2017

cc @rust-lang/lang are we happy to add this? Given that we allow cfg on struct fields in decls, it makes sense to me to allow them in struct lits. This seems minor enough that we don't need an RFC. Thoughts?

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Jan 4, 2017
@nrc
Copy link
Member

nrc commented Jan 4, 2017

Hmm, actually this allows all attributes on fields in struct lits. That still seems ok to me, but not as much of a slam dunk as I thought.

@nrc
Copy link
Member

nrc commented Jan 4, 2017

@Ralith the code looks OK to me, it does need a few tests though.

@Ralith
Copy link
Contributor Author

Ralith commented Jan 4, 2017

Re: other attributes: Would it make sense to be more restrictive? Are there any other locations in the language where cfg is permitted but other attributes are not?

Beginning work on tests now.

@aturon
Copy link
Member

aturon commented Jan 4, 2017

@nrc I'm in favor of this change. In general, we've been moving to add attributes everywhere unless there's ambiguity/hard to discern meaning (as we saw with expressions).

@Mark-Simulacrum
Copy link
Member

Is using cfg on expressions and/or statements stable? It seems like this might cause problems if that isn't available; since that would require duplicating entire functions for each platform combination.

In general, though, I'm in favor. I'm slightly concerned we're moving in the direction of C/C++, where libraries like OpenSSL have a large quantity of fields being "optional" and disabled on certain platforms and/or with certain features, which to some extent heightens complexity for users. I believe @sfackler might have some relevant thoughts on this as he manages the bindings for OpenSSL.

@sfackler
Copy link
Member

sfackler commented Jan 4, 2017

This doesn't really matter for rust-openssl since we never actually create those structs. I definitely agree that conditionally defining public-facing fields is a pain, though doing that for enum variants is fairly common. On the other hand, conditionally defining internal fields can be pretty nice (e.g. https://github.com/brson/error-chain/blob/master/src/lib.rs#L458)

@Ralith
Copy link
Contributor Author

Ralith commented Jan 4, 2017

For illustration, this patch was inspired by the awkwardness of my employing exactly that pattern in my experimental tokio-based window/input system binding, in which I'm trying to provide a uniform interface dispatching over a set of concrete implementations that differs between platforms. Perhaps there's a better way to accomplish this, but this certainly seems to be the most obvious.

Notably, this change alone reduces the need for duplicating entire functions (e.g. init_evdev) in the linked code, though I was able to keep the overhead linear in the number of backends.

@nikomatsakis
Copy link
Contributor

I am in favor of this change.

@nikomatsakis
Copy link
Contributor

@rfcbot fcp merge

The proposal here is to allow attributes on struct fields. This is a relatively small extension to the general trend of allowing attributes on statements and so forth. @nrc is proposing that we approve this (with a feature gate, naturally) without an FCP. I'm in favor since an RFC feels like overkill and we have a big enough backlog as it is. What do you think, @rust-lang/lang?

@rfcbot
Copy link

rfcbot commented Jan 4, 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.

@nrc
Copy link
Member

nrc commented Jan 5, 2017

Re: other attributes: Would it make sense to be more restrictive? Are there any other locations in the language where cfg is permitted but other attributes are not?

I do not think this makes sense, we should allow all attributes, including cfg (sorry for the confusion in my earlier comments).

@Ralith could you add a feature gate to this PR please?

@Ralith
Copy link
Contributor Author

Ralith commented Jan 5, 2017

Added a feature gate struct_field_attributes and added previously missing adjustments to Visitor to walk the new attribute location.

@@ -659,6 +659,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Struct(ref path, ref fields, ref optional_base) => {
visitor.visit_path(path, expression.id);
for field in fields {
walk_list!(visitor, visit_attribute, field.attrs.iter());
Copy link
Contributor

@jseyfried jseyfried Jan 5, 2017

Choose a reason for hiding this comment

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

I believe field.attrs.iter() can be just field.attrs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It cannot. ThinVec does not implement IntoIterator. &ThinVec also does not, leaving only this approach.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh right -- field.attrs.deref() should work then, or just leave as is.

@pnkfelix
Copy link
Member

pnkfelix commented Jan 5, 2017

@rfcbot concern struct-fields-in-patterns

Is the proposed extension intended to cover fields in patterns as well? I cannot tell from skimming the code, and in any case I do not see any test coverage of fields in patterns.

By that "fields in patterns" mean the following:

struct S { x: i32, y: i32 }
fn main() {
    let s = S { x: 10, y: 20 };
    match s {
        S {
            x: 11, // this is a field in a pattern ...
            y: the_y // ... and this is a field in a binding pattern
        } => println!("the_y: {}", the_y),
        
        S { x: the_x, y: 20 } => println!("the_x: {}", the_x),
        
        S { .. } => {}
    }
}

(and of course such patterns can also be used with let, if let , et cetera)

@nikomatsakis
Copy link
Contributor

@pnkfelix good question! Seems like the answer should be "yes, those ought to be supported too", right? I guess I expect fields to be configurable at all places where fields appear (definition, struct literal, patterns)

@Ralith
Copy link
Contributor Author

Ralith commented Jan 6, 2017

@pnkfelix That was not the intention, but I agree that it makes sense. I'm working on implementing it now. Getting #[cfg(...)] working there is taking slightly more effort than expected.

@Ralith Ralith changed the title syntax: configurable struct literal fields syntax: enable attributes and cfg on struct fields Jan 7, 2017
.collect();
ast::ExprKind::Struct(path, fields, base)
}
expr_kind => expr_kind,
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: _ => expr_kind,

match pat_kind {
ast::PatKind::Struct(path, fields, etc) => {
let fields = fields.into_iter()
.filter_map(|a| self.configure_struct_pat_field(a))
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe you could derive HasAttrs for Spanned<ast::FieldPat>, remove configure_struct_pat_field, and gated feature check in this closure.

self.configure(node).map(|node| Spanned { span: span, node: node })
}

pub fn configure_pat_kind(&mut self, pat_kind: ast::PatKind) -> ast::PatKind {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this would be easier to use as configure_pat.

let mut pattern = pattern.unwrap();
pattern.node = self.configure_pat_kind(pattern.node);
println!("{:#?}", pattern);
fold::noop_fold_pat(P(pattern), self)
Copy link
Contributor

Choose a reason for hiding this comment

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

It's more efficient and idiomatic to do the following here:

pattern.map(|mut pattern| {
    // ...
    pattern
})

(if you did configure_pat instead of configure_pat_kind, the .map would be there)

@@ -679,15 +679,18 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
}

fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
let mut pat = pat.unwrap();
pat.node = self.cfg.configure_pat_kind(pat.node);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, also would be solved by having configure_pat.

@jseyfried jseyfried assigned jseyfried and unassigned nrc Jan 8, 2017
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.

@Ralith thanks!
Could you add a feature gate test?
Other than that, r=me pending #38814 (comment).

"attributes on struct literal fields are unstable");
err.emit();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps we could refactor out this gated feature check (and the one below) into self.visit_struct_field_attrs() (c.f. self.visit_expr_attrs()) and then refactor away self.configure_struct_expr_field() and self.configure_struct_pat_field()?

.collect();
pattern.node = ast::PatKind::Struct(path, fields, etc);
}
_ => (),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: _ => {} is more idiomatic (or if let)

@jseyfried
Copy link
Contributor

This PR is blocked on #38814 (comment).
@pnkfelix This now supports #[cfg] on struct field patterns.
cc @withoutboats

@pnkfelix
Copy link
Member

@rfcbot resolved struct-fields-in-patterns

@pnkfelix
Copy link
Member

@rfcbot reviewed

@withoutboats
Copy link
Contributor

@rfcbot reviewed

@jseyfried
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Jan 12, 2017

📌 Commit 7972c19 has been approved by jseyfried

@bors
Copy link
Contributor

bors commented Jan 12, 2017

⌛ Testing commit 7972c19 with merge 78ccca8...

@bors
Copy link
Contributor

bors commented Jan 12, 2017

💔 Test failed - status-travis

@alexcrichton
Copy link
Member

alexcrichton commented Jan 12, 2017 via email

@bors
Copy link
Contributor

bors commented Jan 12, 2017

⌛ Testing commit 7972c19 with merge e357178...

bors added a commit that referenced this pull request Jan 12, 2017
syntax: enable attributes and cfg on struct fields

This enables conditional compilation of field initializers in a struct literal, simplifying construction of structs whose fields are themselves conditionally present. For example, the intializer for the constant in the following becomes legal, and has the intuitive effect:

```rust
struct Foo {
    #[cfg(unix)]
    bar: (),
}

const FOO: Foo = Foo {
    #[cfg(unix)]
    bar: (),
};
```

It's not clear to me whether this calls for the full RFC process, but the implementation was simple enough that I figured I'd begin the conversation with code.
@bors
Copy link
Contributor

bors commented Jan 12, 2017

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

@bors bors merged commit 7972c19 into rust-lang:master Jan 12, 2017
@jseyfried jseyfried added relnotes Marks issues that should be documented in the release notes of the next release. and removed relnotes Marks issues that should be documented in the release notes of the next release. labels Jan 17, 2017
@colin-kiegel
Copy link

I couldn't find a tracking issue to stabilise this. How is the procedure for stabilisation? This would be really nice to have! :-)

@mbrubeck mbrubeck added B-unstable Blocker: Implemented in the nightly compiler and unstable. and removed B-unstable Blocker: Implemented in the nightly compiler and unstable. labels May 1, 2017
@mbrubeck
Copy link
Contributor

mbrubeck commented May 1, 2017

Submitted #41681 as a tracking issue for stability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.