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

RFC: #[attribute]s galore #2602

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

Centril
Copy link
Contributor

@Centril Centril commented Nov 26, 2018

🖼️ Rendered

📝 Summary

Permit attributes to be attached to lifetimes, types, bounds, and constraints as well as associated type equality constraints Foo<#[attr] Assoc = MyType>. For example, in a hypothetical version of proptest one may write:

#[proptest]
// #[quickcheck] could also work similarly.
fn addition_commutes(
    // Run this test for (u8, u8), (u16, u16), and (u32, u32);
    // This is interpreted by `#[proptest]`.
    (a, b): #[types(T, u8, u16, u32)] (T, T)
) -> bool {
    a + b == b + a
}

// Accept x: u8 and vec: Vec<{ y: u8 | y > x }>.
// This is then verified by an external tool.
fn refined(x: u8, vec: Vec<#[require = "> x"] u8>) { ... }

💖 Thanks

To @petrochenkov and @alexreg for their reviews of the draft proposal.

@Centril Centril added T-lang Relevant to the language team, which will review and decide on the RFC. A-syntax Syntax related proposals & ideas A-cfg Conditional compilation related proposals & ideas A-attributes Proposals relating to attributes labels Nov 26, 2018
Copy link

@l4l l4l left a comment

Choose a reason for hiding this comment

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

I do like the feature itself, it is very powerful from one hand, especially for the amount of boilerplate that can be reduced. From the other I concerned about the possible code complexity, since the code might become really cryptic (as in your artificial example). So I would like to ask about more use-cases particularly for the following list of grammar position (names from example):

  • orange_juice
  • icecream/upsilon/omega

text/0000-attributes-galore.md Outdated Show resolved Hide resolved
@alercah
Copy link
Contributor

alercah commented Jan 2, 2019

Your #[linear] example involves a procedural macro which expands to nothing so that an external tool can understand it. But that requires the external tool to operate on pre-macro expansion code, doesn't it, to find the #[linear] attributes? What if the #[linear] attribute is introduced by another macro, how will the tool detect it?

@Centril
Copy link
Contributor Author

Centril commented Jan 3, 2019

@l4l

From the other I concerned about the possible code complexity, since the code might become really cryptic (as in your artificial example).

I think artificial is the operative word here. The example referenced is for demonstrating new capabilities, I don't think any real code would even come close to that level of attribute density.

So I would like to ask about more use-cases particularly for the following list of grammar position (names from example):

orange_juice

type Beta = #[size = "< 42"] Vec<#[orange_juice] Alpha>;

The use cases are the same as for examples: #[types(T, u8, u16, u32)], Vec<#[require = "> x"] u8>, #[repr(C)] { foo: u16, bar: u16 }, and etc. This falls naturally out of being able to annotate types with attributes. Another use case is conditionally taking away applied types to some type constructor, e.g. MyType<u8, #[cfg(foo)] u16>, which can be useful if the second position to exist on some platforms for example.

omega

Same as for #[orange_juice], in particular wrt. cfg.

upsilon

The idea and motivation is the same as for #[eta], #[theta], and #[chi]. Instead of having to conditionally compile an entire function to add or remove a certain bound, we can simply conditionally add the bound itself by writing Foo: #[cfg(bar)] Baz + Quux. If bar does not hold, then you get Foo: Quux.

icecream

This is primarily done because once you admit #[omega] then admitting #[icecream] gives a more consistent experience. IOW, if you can, for the sake of conditional compilation, write Foo<'a, #[omega] 'b>, then allowing &#[icecream]'a T is extending attributes to lifetimes not just in type constructors but also in general. As for concrete use cases of #[icecream], one can imagine encoding extra information, that cannot be said in the type system, for being more clear in conjunction with unsafe.

@alercah

Your #[linear] example involves a procedural macro which expands to nothing so that an external tool can understand it. But that requires the external tool to operate on pre-macro expansion code, doesn't it, to find the #[linear] attributes? What if the #[linear] attribute is introduced by another macro, how will the tool detect it?

As always with macros, they have certain limitations due to solely dealing with syntax. However, the tool could 1) operate on pre-macro expansion code, and 2) at the same time run its own expansion engine and if #[linear] is found in expansions it will collect the places it occurs. Really, a tool can do whatever it wants, including being a fork of a rust compiler. This is not particular to the position in which #[linear] occurs, but to macros in general. Indeed one could imagine writing:

#[linear(ImportantType)]
fn foo(x: ImportantType) {
    // ERROR! We didn't use `x` as promised.
}

@graydon
Copy link

graydon commented Jan 12, 2019

Opposed. Cognitive and implementation costs exceed putative benefits. The syntax was never meant to be nested or generalized. Should remain special case.

@Centril
Copy link
Contributor Author

Centril commented Jan 12, 2019

@graydon

Cognitive and implementation costs exceed putative benefits.

Could you elaborate? What cognitive costs do you foresee here for someone who has already learned the attribute syntax? The implementation costs as far as I can see in libsyntax do not seem particularly complicated to me as compared to the benefits. You say putative, why? Is there some reason you believe the benefits not to be actual? Is there some specific instance, where attributes are now allowed, that you find more objectionable than others in this proposal? Are they all equally objectionable?

The syntax was never meant to be nested or generalized. Should remain special case.

I don't think meant to is particularly interesting here. It has been generalized multiple times afaik because people have found it useful to do so to satisfy real world applications. Therefore "remain" is not entirely accurate. It can only not become more general.

@graydon
Copy link

graydon commented Jan 12, 2019

They are presently restricted to several special cases. IMO they should remain as such unless strong evidence motivates pushing them into new classes.

The cognitive load comes from the user running into code that uses the new productions in ways they had not seen before, that they previously didn't have to encounter / parse / elaborate in their head. See the term "cryptic" used in the first comment above. In general: having a more complex language to read. The proposal even says as much: "This proposal complicates the grammar of Rust".

I expect you'll respond by an appeal to uniformity or consistency here, as the user now doesn't have to remember the special cases where attributes are or are-not allowed; to which I can only say: consistency and uniformity of that sort does not make a language easier on users. Languages that let you put everything in every context are not easier for humans, they're harder. The reader gets fewer fixed points of reference in their comprehension of the text, less can be predicted or assumed, they have to work harder because there's more room for different productions to still be meaningful. Consistency in the sense of not adding meaningless variation is good for comprehension, sure; consistency in the sense of generalizing rules of combination to allow more legal combinations than necessary is bad for comprehension.

Re: "putative": motivation case 1 is on a structural record type which I've argued elsewhere shouldn't exist. Motivation 2 is for nonexistent static analysis tools that might someday exist. Motivation 3 is from a multiplexing procedural macro in the introductory example plainly labeled as "hypothetical". I think it's fair and maybe even a bit strong to call these cases "putative".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Proposals relating to attributes A-cfg Conditional compilation related proposals & ideas A-syntax Syntax related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants