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

Attribute inheritance causes analysis failures for certain built-in attributes #24319

Open
brandjon opened this issue Nov 13, 2024 · 2 comments
Assignees
Labels
P1 I'll work on this now. (Assignee required) team-Loading-API BUILD file and macro processing: labels, package(), visibility, glob type: bug

Comments

@brandjon
Copy link
Member

brandjon commented Nov 13, 2024

The new attribute inheritance system handles attributes with computed defaults by setting their value to None within a symbolic macro. That way, when they're passed through to the wrapped rule, the computed-default logic considers it to be missing and computes its value as intended.

But some attributes that are morally computed defaults are not in fact implemented that way. The timeout attribute of test rules is one example. restricted_to is another (that one only fails at analysis time).

A quick solution is to hardcode a list of these badly behaved builtin attributes and None-ify them all. Though this might require a backwards incompatible change to fix their behavior in the future.

Edit: Correction: restricted_to causes it to fail at loading time, not analysis. timeout works for starlark-defined rules but not native ones, because it's specified as a computed default only for starlark rules. Yet we also found a crash bug here, when inheriting attributes of an unexported rule. We think a good fix is to hardcode problematic attribute names in MacroClass#forceDefaultToNone, and only in cases where the Attribute does not have the property flag STARLARK_DEFINED (to avoid triggering on user-defined attrs of the same name). Alternatively, we could give some of these problematic attributes trivial computed defaults, but that seems like it could subtly break things (query output or native.existing_rules perhaps).

@brandjon brandjon added type: bug P1 I'll work on this now. (Assignee required) team-Loading-API BUILD file and macro processing: labels, package(), visibility, glob labels Nov 13, 2024
@brandjon
Copy link
Member Author

@bazel-io fork 8.0.0

copybara-service bot pushed a commit that referenced this issue Nov 14, 2024
…al_enable_macro_inherit_attrs flag

We still have open questions about how macro attribute inheritance ought to
interact with the tracking of whether rule attributes were or were not
explicitly provided.

In effect, this re-opens #24066
Part of addressing #24319

RELNOTES: symbolic macro attribute inheritance is now marked experimental; set --experimental_enable_macro_inherit_attrs flag to enable it.
PiperOrigin-RevId: 696582223
Change-Id: I3d7cb434bf8fe2da9cd10019e6075990205e7153
@brandjon
Copy link
Member Author

Our discussion of this issue turned into a tangled web of confusion around the meaning of None and default values in macros, but we think we have a reasonably elegant solution now:

  1. Within macros, but not rules, attribute descriptors (attr.string(), etc.) are now allowed to pass default=None. (Previously the only commonly used attr type that could do this was attr.label().) This lets a macro opt into declaring that it can see when one of its attributes was not explicitly set. (This is already possible for attr.label(), since my_label_attr = None counts as not passing anything in at all.) Having this feature is conceptually important if macros are ever going to help deprecate the concept of computed defaults.

  2. For inherited attributes, the default of the descriptor is always overridden to be None. Doesn't matter if you're inheriting a computed default, or a simple default="abc" -- the macro will see None if the caller didn't pass anything in.

  3. For configurable attributes, None gets promoted to a trivial select, select({"//conditions:default": None}). (We really need a more concise stringification syntax for that -- maybe select(None)?). When passing this to either a rule or a macro, this gets unwrapped as my_attr = select({"//conditions:default": None}) -> my_attr = None -> dropped. So you see a select value inside your macro, but the rule or macro you're instantiating doesn't get passed anything, and it's not considered explicitly set. Thus, computed defaults execute as intended, it's omitted from bazel query inspection, etc.

This would seem to solve the direct bugs raised in this issue about crashes when inheriting built-in attributes. It also maintains the nice property that macro authors don't need to worry about None being a distinct value, at least for non-inherited attributes, unless they specifically opt into that as their default or they are trying to manipulate an inherited attribute. And when they do see a None in a configurable attribute, it's still a select() just like anything else. Furthermore, when migrating legacy macros to symbolic, we avoid a regression where all the **kwargs attributes seem to be explicitly set on the rule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P1 I'll work on this now. (Assignee required) team-Loading-API BUILD file and macro processing: labels, package(), visibility, glob type: bug
Projects
None yet
Development

No branches or pull requests

2 participants