-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add doesCastPreserveOwnershipForType() to SIL/Utils/DynamicCasts.cpp #41952
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
This is the first step in fixing the OwnershipForwardingMixin. I'm not sure how to test this step yet. |
408476e
to
427fba1
Compare
@swift-ci test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice!
include/swift/AST/Types.h
Outdated
@@ -1246,6 +1251,10 @@ class alignas(1 << TypeAlignInBits) TypeBase | |||
/// Whether this is the AnyObject type. | |||
bool isAnyObject(); | |||
|
|||
/// Return true if this type will never be an AnyObject existential after | |||
/// substitution. | |||
bool isNotPotentiallyAnyObject(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: IMO it's better to avoid the negation in the function name -> isPotentiallyAnyObject
.
lib/SIL/Utils/DynamicCasts.cpp
Outdated
// compatible cast cannot release any references within its operand's value | ||
// and cannot retain any references owned by its result. | ||
// | ||
// An type's ownership is preserved by a dynamic cast if it is both |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"A type's ownership..."
lib/SIL/Utils/DynamicCasts.cpp
Outdated
return true; | ||
} | ||
return doesCastPreserveOwnershipForType(getSourceFormalType()) | ||
&& doesCastPreserveOwnershipForType(getTargetFormalType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand this code correctly, it's more conservative than what you described in the comment with (A), (B1) and (B2). Maybe you add a short comment for these conditions to relate them to A, B1, B2.
Specifically, does this include casting a class existential to a class which conforms to that protocol?
I'm asking because this is an important use case in libswift for fast casting (#41784).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the comments:
// A type's ownership is preserved by a dynamic cast if it is neither
// (A) a potentially bridged value
// (isPotentiallyBridgedValueOrExistentialType())
// nor
// (B) potentially wrapped in a transparent type
// (isPotentiallyAnyObject())
A non-AnyObject class existential meets both requirements, as does a nominal class. I'll make sure there's some kind of test for that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eeckstein I've lost track of the number of rewrites, but with the last attempt I think it's reaching the point where anyone can understand it. Still working on tests for all the interesting cases
0ce5011
to
690cd32
Compare
@swift-ci test |
690cd32
to
51af7e9
Compare
@swift-ci test |
@swift-ci test source compatibility |
@swift-ci benchmark |
51af7e9
to
52f3413
Compare
@swift-ci test |
@swift-ci benchmark |
lib/AST/Type.cpp
Outdated
bool TypeBase::isPotentiallyAnyObject() { | ||
Type unwrappedTy = lookThroughAllOptionalTypes(); | ||
if (auto archetype = unwrappedTy->getAs<ArchetypeType>()) { | ||
return !archetype->hasRequirements(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasRequirements() is a weird predicate. It doesn't check for all requirements, only conformance and superclass, and it's only used in one other place. Maybe you should just spell it out here so we can remove hasRequirements()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. Here:
bool TypeBase::isPotentiallyAnyObject() {
Type unwrappedTy = lookThroughAllOptionalTypes();
if (auto archetype = unwrappedTy->getAs<ArchetypeType>()) {
// Does archetype have any requirements that contradict AnyObject?
// 'T : AnyObject' requires a layout constraint, not a conformance.
return archetype->getConformsTo().empty() && !archetype->getSuperclass();
}
return unwrappedTy->isAnyObject();
}
if (auto archetype = unwrappedTy->getAs<ArchetypeType>()) { | ||
return !archetype->hasRequirements(); | ||
} | ||
return unwrappedTy->isAnyObject(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want this to be true for Any as well as AnyObject?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AnyObject is very different from Any as regards casting behavior, so I suspect Andy wants this to only be true for AnyObject.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@slavapestov Good question. If we cared about Any
, then the API would need to be called isAnyOrAnyObject()
. I don't think it's possible for an Any
to be AnyObject
. That would imply that an existential contains another existential. I noticed the type system code does an Any-or-AnyObject checks in some places, but this API is specifically for potentially AnyObject
.
The client of this API cares about cases like this. What's important to the client is that only AnyObject
can hold a __SwiftValue
. Any
is never a __SwiftValue
:
class C: Hashable, Equatable, P {
static func == (lhs: C, rhs: C) -> Bool {
return true
}
func hash(into hasher: inout Hasher) {}
}
let val: AnyHashable = C()
let anyObject = val as AnyObject // metatype == "class __SwiftValue"
let any = val as Any // metatype == "struct AnyHashable"
Added an AST helper in Types.h: - isPotentiallyAnyObject() This formalizes logic for when cast operations forward ownership. Various OSSA optimization rely on this for correctness. This fixes latent bugs throughout the optimizer. I was compelled to fix this now because we want to make OSSA optimizations across dynamic casts more aggressive. For example, we want to optimize retain/release across enum formation.
52f3413
to
90ec837
Compare
@swift-ci smoke test |
Add doesCastPreserveOwnershipForType() to SIL/Utils/DynamicCasts.cpp
Added helpers in the AST Types.h
isNotPotentiallyAnyObject()
isPotentiallyBridgedValueOrExistentialType()
This formalizes logic for when cast operations forward
ownership. Various OSSA optimization rely on this for
correctness. This may fixes latent bugs throughout the optimizer.
I was compelled to fix this now because we want to make OSSA
optimizations across dynamic casts more aggressive. For example, we
want to optimize retain/release across enum formation.