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

[APINotes] Add support for bounds safety annotations #9822

Merged
merged 7 commits into from
Jan 17, 2025

Conversation

hnrklssn
Copy link

This adds support for annotating function parameters with __counted_by, __sized_by, __counted_by_or_null, __sized_by_or_null, and __ended_by, using API notes. The main content of handlePtrCountedByEndedByAttr is extracted to applyPtrCountedByEndedByAttr and decoupled from ParsedAttr. The helper function ParseBoundsAttributeArgFromString is added to make it possible to parse count expressions from SemaAPINotes.

The current implementation of __terminated_by/__null_terminated makes it harder to extract from the iterative type processing, but since it doesn't require any extra context to parse the attribute, it can be applied using the normal Type override instead.

rdar://139830881

This adds support for annotating function parameters with __counted_by,
__sized_by, __counted_by_or_null, __sized_by_or_null, and __ended_by,
using API notes. The main content of handlePtrCountedByEndedByAttr is
extracted to applyPtrCountedByEndedByAttr and decoupled from ParsedAttr.
The helper function ParseBoundsAttributeArgFromString is added to make
it possible to parse count expressions from SemaAPINotes.

The current implementation of __terminated_by/__null_terminated makes it
harder to extract from the iterative type processing, but since it
doesn't require any extra context to parse the attribute, it can be
applied using the normal Type override instead.

rdar://139830881
@hnrklssn
Copy link
Author

@swift-ci please smoke test

@egorzhdan
Copy link

egorzhdan commented Jan 13, 2025

Are these attributes supported in upstream Clang? If so, this PR should probably be upstream.

@rapidsna
Copy link

Could you please add /* TO_UPSTREAM(BoundsSafety) ON/OFF */ for changes that need to be upstreamed?

BoundsSafety:
Kind: counted_by
Level: 1
BoundedBy: "*len"

Choose a reason for hiding this comment

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

Nice!

@rapidsna
Copy link

It may worth adding some more tests:

  • Tests to add APINotes on functions that already have some Nullability attributes.
  • Diag tests for APINotes that create invalid bounds attributes.
  • Tests to add both bounds and noescape attributes to make sure both are added as expected.

@hnrklssn
Copy link
Author

Are these attributes supported in upstream Clang? If so, this PR should probably be upstream.

__ended_by and __terminated_by are not upstream yet, and the other attributes are not the full implementation yet.

Copy link

@Xazax-hun Xazax-hun left a comment

Choose a reason for hiding this comment

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

I think the debug dumps might be missing, e.g., updating ParamInfo::dump.

clang/lib/APINotes/APINotesReader.cpp Outdated Show resolved Hide resolved
@@ -8941,6 +8941,88 @@ TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context,
return Result;
}

ExprResult
Parser::ParseBoundsAttributeArgFromString(StringRef ExprStr, StringRef Context,

Choose a reason for hiding this comment

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

I am not sure how heavy weight this is, do you think it would make sense to only spin all of these up once per declaration instead of once per annotation? Feel free to leave as is for now but maybe we could leave a fixme or todo in case this shows up in a profile.

Copy link
Author

Choose a reason for hiding this comment

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

I think it's tricky, because IIUC the Lexer is immutably tied to a FileID, which is tied to a read-only MemoryBuffer, so we have to construct new ones for each string. In ClangImporter we get around this by caching the parse result of e.g. the swift_attr strings. Of course, even if the string content we're parsing would be the exact same, the resulting DeclRefs would vary depending on the surrounding scope, so we'd have to flush the cache for each new declaration. I don't think we'd have enough cache hits for identical attribute arguments within a single declaration to warrant it.

Choose a reason for hiding this comment

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

Oh, I see.

AttrName = "__sized_by_or_null";
Kind = AttributeCommonInfo::AT_SizedByOrNull;
break;
case BoundsSafetyKind::EndedBy:

Choose a reason for hiding this comment

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

I think we should have a default branch with unreachable here.

Copy link
Author

Choose a reason for hiding this comment

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

I prefer leaving it out for switches that are expected to be exhaustive, so that we get warnings if a new enum option is updated without updating the switch; -Wswitch doesn't fire when there is a default case. I can add a range assert before the switch.

@hnrklssn
Copy link
Author

@swift-ci please smoke test

Copy link

@Xazax-hun Xazax-hun left a comment

Choose a reason for hiding this comment

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

Overall looks good to me. I think there might be a couple extra scenarios we might want to test. Particularly cases when the API already has bounds safety annotations but the same API is also annotated via APINotes. Do we get duplicated annotations? What if the annotations contradict each other? I think these scenarios can happen when someone is using APINotes to annotate 3rd party headers, but a newer version of those headers will introduce these annotations at the source.

@hnrklssn
Copy link
Author

@swift-ci please test

@hnrklssn
Copy link
Author

Overall looks good to me. I think there might be a couple extra scenarios we might want to test. Particularly cases when the API already has bounds safety annotations but the same API is also annotated via APINotes. Do we get duplicated annotations? What if the annotations contradict each other? I think these scenarios can happen when someone is using APINotes to annotate 3rd party headers, but a newer version of those headers will introduce these annotations at the source.

Normally multiple annotations result in an error (and they do here also), however there are certain exceptions where e.g. a redeclaration is allowed to have the same attributes as the original declaration, provided that the attribute args match. Added an exception to let API notes have redundant attributes, again provided that they match.

@delcypher delcypher added the clang:bounds-safety Issue relating to the experimental -fbounds-safety feature in Clang label Jan 16, 2025
/* TO_UPSTREAM(BoundsSafety) ON */
class BoundsSafetyInfo {
public:
enum class BoundsSafetyKind {

Choose a reason for hiding this comment

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

Doesn't this enum basically exist elsewhere? Should we be duplicating it here?

Copy link
Author

@hnrklssn hnrklssn Jan 16, 2025

Choose a reason for hiding this comment

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

Originally I was thinking about including __terminated_by, __single, __bidi_indexable etc, and I think there's still a possibility that those may be added later, especially __terminated_by. More importantly, this enum representation is now used in the binary encoding of API notes, so I think duplicating it is fine to keep them uncoupled.

@hnrklssn hnrklssn merged commit da945ea into swiftlang:stable/20240723 Jan 17, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:bounds-safety Issue relating to the experimental -fbounds-safety feature in Clang
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants