Skip to content

[ClangImporter] Don't crash when a bad override affects NSErrors. #7907

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

Conversation

jrose-apple
Copy link
Contributor

Most of the time the name importer does a good job deciding whether to import a particular method as throwing or not. However, when a method is an override, it skips all that work and assumes the decisions made for the superclass method apply here as well—which makes sense, since you're going to get the subclass implementation if you call the superclass's entry point. This can really throw things off if the types don't match up, though. Handle the one case where this is legal according to the rules of Objective-C, and make sure we don't import methods in the other cases.

rdar://problem/30705461

@jrose-apple jrose-apple requested a review from rjmccall March 4, 2017 04:45
@jrose-apple
Copy link
Contributor Author

@swift-ci Please test

// Check for a bad override.
if (resultTy->isVoid())
return Type();
return resultTy;
Copy link
Contributor

Choose a reason for hiding this comment

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

does this mean that if a non-nil return type is returned the error is checked first in the generated code for the NSError?

e.g.

- (nonnull id)foo:(NSError **_Nullable)error;
...

NSError *error;
id result = [something foo:&error];
if (error) { 
    assumeItFailed(); 
} else {
    useResult(result);
} 

If so that might cause unexpected failures of un-initialized memory cases when passing into existing failure methods.

Or is this just for the type checker?

Copy link
Member

Choose a reason for hiding this comment

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

We do have an Objective-C attribute form for that (swift_error(nonnull_error)), but we never infer it in the Clang importer specifically because we were concerned about the uninitialized memory cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. I'll double-check that the generated code is still going to look for a nil value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

(Good question, to be sure!)

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 does indeed still check for a nil return, without looking at the error.

// Check for a bad override.
if (resultTy->isVoid())
return Type();
return resultTy;
Copy link
Member

Choose a reason for hiding this comment

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

We do have an Objective-C attribute form for that (swift_error(nonnull_error)), but we never infer it in the Clang importer specifically because we were concerned about the uninitialized memory cases.

// represent that, but it shouldn't fall over either.
if (resultTy->isAnyClassReferenceType()
|| resultTy->is<AnyFunctionType>()
|| resultTy->getAnyPointerElementType())
Copy link
Member

Choose a reason for hiding this comment

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

This is an odd set of conditions that excludes bridged types. adjustResultTypeForThrowingFunction 's caller allows bridging, so an NSString * _Nonnull result type would fail to import (because String doesn't meet any of those conditions) while an NSObject * _Nonnull result type would smooth over the issue in the ObjC headers.

If we're not going to rigorously check for consistency, then an isVoid check probably suffices here as in the other places.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mm, good point.

Most of the time the name importer does a good job deciding whether to
import a particular method as throwing or not. However, when a method
is an override, it skips all that work and assumes the decisions made
for the superclass method apply here as well---which makes sense,
since you're going to get the subclass implementation if you call the
superclass's entry point. This can really throw things off if the
types /don't/ match up, though. Handle the one case where this is
legal according to the rules of Objective-C, and make sure we don't
import methods in the other cases.

rdar://problem/30705461
@jrose-apple jrose-apple force-pushed the undeserved-assertions-for-erroneous-errors branch from 4ac3fd7 to 48db841 Compare March 6, 2017 18:40
@DougGregor
Copy link
Member

Latest version LGTM, thank you!

let _: String = try obj.produceRiskyString()

let _: NSObject = try obj.badNullResult()
let _: CInt = try obj.badNullResult2() // This is unfortunate but consistent.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@DougGregor, note this change. I checked the generated IR and there's not even a zero check here, because we treat "null" and "zero" as different error conventions (probably because of bridged types). But we don't crash, and the original code is arguably wrong. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

The original code is wrong. Maybe Clang should be diagnosing the bogus override (an int instead of an object pointer? seriously?), but I think our generated IR is reasonable.

@jrose-apple
Copy link
Contributor Author

@swift-ci Please smoke test

@jrose-apple jrose-apple merged commit c4b66e5 into swiftlang:master Mar 6, 2017
@jrose-apple jrose-apple deleted the undeserved-assertions-for-erroneous-errors branch March 6, 2017 19:53
jrose-apple added a commit to jrose-apple/swift that referenced this pull request Mar 6, 2017
…iftlang#7907)

Most of the time the name importer does a good job deciding whether to
import a particular method as throwing or not. However, when a method
is an override, it skips all that work and assumes the decisions made
for the superclass method apply here as well---which makes sense,
since you're going to get the subclass implementation if you call the
superclass's entry point. This can really throw things off if the
types /don't/ match up, though. Handle the one case where this is
legal according to the rules of Objective-C, and make sure we don't
import methods in the other cases.

rdar://problem/30705461
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants