Skip to content

Fix the SIL type equality assert failures due to opaque return types. #70865

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/SIL/SILType.h
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,10 @@ class SILType {
return value.getOpaqueValue() != rhs.value.getOpaqueValue();
}

// Returns true if the SILTypes are equal with the opaque return
// types being considered to be equal to their underlying types.
bool isEqualWithOpaqueReturnTypes(SILType rhs) const;

/// Return the mangled name of this type, ignoring its prefix. Meant for
/// diagnostic purposes.
std::string getMangledName() const;
Expand Down
58 changes: 58 additions & 0 deletions lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "swift/AST/Module.h"
#include "swift/AST/SemanticAttrs.h"
#include "swift/AST/Type.h"
#include "swift/AST/TypeDifferenceVisitor.h"
#include "swift/SIL/AbstractionPattern.h"
#include "swift/SIL/SILFunctionConventions.h"
#include "swift/SIL/SILModule.h"
Expand Down Expand Up @@ -1253,3 +1254,60 @@ SILType::conformsToProtocol(SILFunction *fn, ProtocolDecl *protocol) const {
bool SILType::isSendable(SILFunction *fn) const {
return getASTType()->isSendableType(fn->getParentModule());
}

class IsEqualWithOpaqueReturnTypes
: public CanTypeDifferenceVisitor<IsEqualWithOpaqueReturnTypes> {

static CanType replaceWithUnderlying(CanType type) {
if (auto primary = type->getAs<PrimaryArchetypeType>()) {
// Unwrap generic args.
return primary->getInterfaceType()->getCanonicalType();
}
auto opaque = type->getAs<OpaqueTypeArchetypeType>();
if (!opaque) {
return type;
}
OpaqueTypeDecl *decl = opaque->getDecl();
SubstitutionMap map;
if (auto maybeMap = decl->getUniqueUnderlyingTypeSubstitutions()) {
map = *maybeMap;
}
if (map.empty()) {
return type;
}
auto replacementTypes =
static_cast<const SubstitutionMap &>(map).getReplacementTypes();
CanType result = type;
auto genericSig = map.getGenericSignature();
auto genericParams = genericSig.getGenericParams();
// The opaque return type comes at the last.
unsigned genericParamIdx = genericParams.size() - 1;
Type replacementType = replacementTypes[genericParamIdx];
if (replacementType) {
result = replacementType->getCanonicalType();
}
return result;
}

public:
bool visit(CanType t1, CanType t2) {
CanType t1_ = replaceWithUnderlying(t1);
CanType t2_ = replaceWithUnderlying(t2);
return CanTypeDifferenceVisitor<
IsEqualWithOpaqueReturnTypes>::template visit(t1_, t2_);
}

bool equal(CanType t1, CanType t2) { return !visit(t1, t2); };
};

bool SILType::isEqualWithOpaqueReturnTypes(SILType rhs) const {
const SILType *lhs = this;
if (*lhs == rhs) {
return true;
}
CanType lhsType = lhs->getRawASTType(), rhsType = rhs.getRawASTType();
if (lhsType == rhsType) {
return true;
}
return IsEqualWithOpaqueReturnTypes().equal(lhsType, rhsType);
}
6 changes: 4 additions & 2 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {

/// Assert that two types are equal.
void requireSameType(SILType type1, SILType type2, const Twine &complaint) {
_require(type1 == type2, complaint,
_require(type1 == type2 || type1.isEqualWithOpaqueReturnTypes(type2),
complaint,
[&] { llvm::dbgs() << " " << type1 << "\n " << type2 << '\n'; });
}

Expand Down Expand Up @@ -6106,7 +6107,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
++argI;
if (bbarg->getType() != mappedTy &&
bbarg->getType() != F.getLoweredType(mappedTy.getASTType())
.getCategoryType(mappedTy.getCategory())) {
.getCategoryType(mappedTy.getCategory()) &&
!bbarg->getType().isEqualWithOpaqueReturnTypes(mappedTy)) {
llvm::errs() << what << " type mismatch!\n";
llvm::errs() << " argument: "; bbarg->dump();
llvm::errs() << " expected: "; mappedTy.dump();
Expand Down
4 changes: 3 additions & 1 deletion lib/Serialization/DeserializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,9 @@ SILValue SILDeserializer::getLocalValue(ValueID Id,
Entry = ::new PlaceholderValue(Type);
}
// If this value was already defined, check it to make sure types match.
assert(Entry->getType() == Type && "Value Type mismatch?");
assert((Entry->getType() == Type ||
Entry->getType().isEqualWithOpaqueReturnTypes(Type)) &&
"Value Type mismatch?");
return Entry;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
public protocol Reducer<State> {
associatedtype State
}

public class WindowData {}

public struct CR<State, R: Reducer>: Reducer
where State == R.State {
}

internal struct SidebarContextMenu<WindowState: WindowData>: Reducer {
typealias State = WindowState
}

public protocol Factory {
associatedtype X: Reducer<WindowData>
func build() -> X
}

public struct MyFactory<WindowState: WindowData>: Factory {
public typealias State = WindowData
public init() {}
public func build() -> some Reducer<WindowData> {
CR<WindowData, SidebarContextMenu<WindowData>>()
}
}
13 changes: 13 additions & 0 deletions test/SIL/Serialization/opaque_return_type_equality.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -c %S/Inputs/opaque_return_type_equality_input.swift -emit-module-path %t/opaque_return_type_equality_input.swiftmodule -I %t -O -module-name opaque_return_type_equality_input -o %t/opaque_return_type_equality_input.o
// RUN: %target-swift-frontend -c %S/opaque_return_type_equality.swift -emit-module-path %t/opaque_return_type_equality.swiftmodule -I %t -O -module-name opaque_return_type_equality -o %t/opaque_return_type_equality.o

// Check that the SIL type equality check asserts don't fail between the opaque return types and their underlying types.

import opaque_return_type_equality_input

public func build<F: Factory>(f: F) -> any Reducer<WindowData> {
return f.build()
}

build(f: MyFactory())