Skip to content

Conversation

@kavon
Copy link
Member

@kavon kavon commented Nov 13, 2025

In some cases, at least with ObjC bridging, we could end up throwing SGF.emitSubstToOrigValue into an infinite recursion through Transform::transform, in the case of the optional-to-optional conversion:

Optional -> Optional

There's special handling for such conversions in SILGen, and the problem is its old recognition of any Sendable being "equivalent" in terms of ABI to Any relied on type equality.

While the types are not equal at compile time, they are at runtime. Plus, they are ABI equivalent.

resolves rdar://163872193

In some cases, at least with ObjC bridging, we could
end up throwing `SGF.emitSubstToOrigValue` into an infinite
recursion through `Transform::transform`, in the case of
the optional-to-optional conversion:

  Optional<any Sendable> -> Optional<Any>

There's special handling for such conversions in SILGen,
and the problem is its old recognition of `any Sendable` being
"equivalent" in terms of ABI to `Any` relied on type equality.

While the types are not equal at compile time, they are at
runtime. Plus, they are ABI equivalent.

resolves rdar://163872193
@kavon
Copy link
Member Author

kavon commented Nov 13, 2025

@swift-ci smoke test

return false;
}

bool ExistentialLayout::isABICompatibleWith(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I follow. There are other situations where two existential types are ABI compatible but not the same type in the compiler, for example P & AnyObject vs P & C for an arbitrary class C. Does the compiler also enter an infinite loop in that case?

init->finishInitialization(SGF);
return init->getManagedAddress();
if (src.getType().isAddress())
return src;
Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like these changes fix compiler_crashers/SILBuilder-emitStoreValueOperation-a20514.swift as well.

auto ct1 = type1.getASTType();
auto ct2 = type2.getASTType();
if (ct1->isExistentialType() && ct2->isExistentialType() &&
ct1->getExistentialLayout().isABICompatibleWith(
Copy link
Contributor

Choose a reason for hiding this comment

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

If dropping marker protocols is the correct fix here, I would prefer you used the existing withoutMarkerProtocols() (which then allows you to compare for canonical type equality after applying it to both sides) instead of adding a new method to ExistentialLayout that is only used in one place. withoutMarkerProtocols() is already used in a number of places in the compiler.

Copy link
Contributor

Choose a reason for hiding this comment

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

I second this, we have a tendency to add a lot of things that essentially do the same thing but with slight differences and it's sometimes really hard to use these APIs. withoutMarkerProtocols seems appropriate here, we use that in the Sema for the same purpose.

Copy link
Contributor

Choose a reason for hiding this comment

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

Although perhaps we should sink it down to TypeBase from ProtocolCompositionType, so that it can properly handle a single ProtocolType that's a marker protocol

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