Skip to content

Commit

Permalink
Merge pull request swiftlang#78272 from xedin/rdar-140300022-6.1
Browse files Browse the repository at this point in the history
[6.1][TypeChecker/SILGen] Allow `any Sendable` to match `Any` while matching generic arguments
  • Loading branch information
xedin authored Dec 20, 2024
2 parents 99a9d61 + fde6f50 commit 91c6425
Show file tree
Hide file tree
Showing 20 changed files with 725 additions and 20 deletions.
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2789,6 +2789,9 @@ ERROR(types_not_inherited_decl,none,
ERROR(types_not_inherited_in_decl_ref,none,
"referencing %kind0 on %1 requires that %2 inherit from %3",
(const ValueDecl *, Type, Type, Type))
ERROR(cannot_reference_conditional_member_on_base_multiple_mismatches,none,
"cannot reference %kind0 on %1",
(const ValueDecl *, Type))
NOTE(where_requirement_failure_one_subst,none,
"where %0 = %1", (Type, Type))
NOTE(where_requirement_failure_both_subst,none,
Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3615,6 +3615,24 @@ class ActorIsolationErasureExpr : public ImplicitConversionExpr {
}
};

/// UnsafeCastExpr - A special kind of conversion that performs an unsafe
/// bitcast from one type to the other.
///
/// Note that this is an unsafe operation and type-checker is allowed to
/// use this only in a limited number of cases like: `any Sendable` -> `Any`
/// conversions in some positions, covariant conversions of function and
/// function result types.
class UnsafeCastExpr : public ImplicitConversionExpr {
public:
UnsafeCastExpr(Expr *subExpr, Type type)
: ImplicitConversionExpr(ExprKind::UnsafeCast, subExpr, type) {
}

static bool classof(const Expr *E) {
return E->getKind() == ExprKind::UnsafeCast;
}
};

/// Extracts the isolation of a dynamically isolated function value.
class ExtractFunctionIsolationExpr : public Expr {
/// The function value expression from which to extract the
Expand Down
3 changes: 2 additions & 1 deletion include/swift/AST/ExprNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ ABSTRACT_EXPR(ImplicitConversion, Expr)
EXPR(LinearFunctionExtractOriginal, ImplicitConversionExpr)
EXPR(LinearToDifferentiableFunction, ImplicitConversionExpr)
EXPR(ActorIsolationErasure, ImplicitConversionExpr)
EXPR_RANGE(ImplicitConversion, Load, ActorIsolationErasure)
EXPR(UnsafeCast, ImplicitConversionExpr)
EXPR_RANGE(ImplicitConversion, Load, UnsafeCast)
ABSTRACT_EXPR(ExplicitCast, Expr)
ABSTRACT_EXPR(CheckedCast, ExplicitCastExpr)
EXPR(ForcedCheckedCast, CheckedCastExpr)
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,9 @@ class alignas(1 << TypeAlignInBits) TypeBase
/// Is this an existential containing only marker protocols?
bool isMarkerExistential();

/// Is this `any Sendable` type?
bool isSendableExistential();

bool isPlaceholder();

/// Returns true if this contextual type does not satisfy a conformance to
Expand Down
6 changes: 6 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2727,6 +2727,12 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
printFoot();
}

void visitUnsafeCastExpr(UnsafeCastExpr *E, StringRef label) {
printCommon(E, "unsafe_cast_expr", label);
printRec(E->getSubExpr());
printFoot();
}

void visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *E,
StringRef label) {
printCommon(E, "extract_function_isolation", label);
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5346,6 +5346,9 @@ void PrintAST::visitLinearToDifferentiableFunctionExpr(swift::LinearToDifferenti
void PrintAST::visitActorIsolationErasureExpr(ActorIsolationErasureExpr *expr) {
}

void PrintAST::visitUnsafeCastExpr(UnsafeCastExpr *expr) {
}

void PrintAST::visitExtractFunctionIsolationExpr(ExtractFunctionIsolationExpr *expr) {
visit(expr->getFunctionExpr());
Printer << ".isolation";
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ ConcreteDeclRef Expr::getReferencedDecl(bool stopAtParenExpr) const {
PASS_THROUGH_REFERENCE(UnderlyingToOpaque, getSubExpr);
PASS_THROUGH_REFERENCE(Unreachable, getSubExpr);
PASS_THROUGH_REFERENCE(ActorIsolationErasure, getSubExpr);
PASS_THROUGH_REFERENCE(UnsafeCast, getSubExpr);
NO_REFERENCE(Coerce);
NO_REFERENCE(ForcedCheckedCast);
NO_REFERENCE(ConditionalCheckedCast);
Expand Down Expand Up @@ -828,6 +829,7 @@ bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
case ExprKind::UnderlyingToOpaque:
case ExprKind::Unreachable:
case ExprKind::ActorIsolationErasure:
case ExprKind::UnsafeCast:
case ExprKind::TypeValue:
// Implicit conversion nodes have no syntax of their own; defer to the
// subexpression.
Expand Down Expand Up @@ -1058,6 +1060,7 @@ bool Expr::isValidParentOfTypeExpr(Expr *typeExpr) const {
case ExprKind::CurrentContextIsolation:
case ExprKind::ActorIsolationErasure:
case ExprKind::ExtractFunctionIsolation:
case ExprKind::UnsafeCast:
return false;
}

Expand Down
11 changes: 11 additions & 0 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ bool TypeBase::isMarkerExistential() {
return true;
}

bool TypeBase::isSendableExistential() {
Type constraint = this;
if (auto existential = constraint->getAs<ExistentialType>())
constraint = existential->getConstraintType();

if (!constraint->isConstraintType())
return false;

return constraint->getKnownProtocol() == KnownProtocolKind::Sendable;
}

bool TypeBase::isPlaceholder() {
return is<PlaceholderType>();
}
Expand Down
25 changes: 25 additions & 0 deletions lib/SILGen/SILGenBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,31 @@ ManagedValue SILGenBuilder::createUncheckedBitCast(SILLocation loc,
return cloner.clone(cast);
}

ManagedValue SILGenBuilder::createUncheckedForwardingCast(SILLocation loc,
ManagedValue value,
SILType type) {
CleanupCloner cloner(*this, value);
SILValue cast = createUncheckedForwardingCast(loc, value.getValue(), type);

// Currently createUncheckedBitCast only produces these
// instructions. We assert here to make sure if this changes, this code is
// updated.
assert((isa<UncheckedTrivialBitCastInst>(cast) ||
isa<UncheckedRefCastInst>(cast) ||
isa<UncheckedValueCastInst>(cast) ||
isa<ConvertFunctionInst>(cast)) &&
"SILGenBuilder is out of sync with SILBuilder.");

// If we have a trivial inst, just return early.
if (isa<UncheckedTrivialBitCastInst>(cast))
return ManagedValue::forObjectRValueWithoutOwnership(cast);

// Otherwise, we forward the cleanup of the input value and place the cleanup
// on the cast value since unchecked_ref_cast is "forwarding".
value.forward(SGF);
return cloner.clone(cast);
}

ManagedValue SILGenBuilder::createOpenExistentialRef(SILLocation loc,
ManagedValue original,
SILType type) {
Expand Down
5 changes: 5 additions & 0 deletions lib/SILGen/SILGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@ class SILGenBuilder : public SILBuilder {
ManagedValue createUncheckedBitCast(SILLocation loc, ManagedValue original,
SILType type);

using SILBuilder::createUncheckedForwardingCast;
ManagedValue createUncheckedForwardingCast(SILLocation loc,
ManagedValue original,
SILType type);

using SILBuilder::createOpenExistentialRef;
ManagedValue createOpenExistentialRef(SILLocation loc, ManagedValue arg,
SILType openedType);
Expand Down
19 changes: 19 additions & 0 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ namespace {
RValue visitCovariantReturnConversionExpr(
CovariantReturnConversionExpr *E,
SGFContext C);
RValue visitUnsafeCastExpr(UnsafeCastExpr *E, SGFContext C);
RValue visitErasureExpr(ErasureExpr *E, SGFContext C);
RValue visitAnyHashableErasureExpr(AnyHashableErasureExpr *E, SGFContext C);
RValue visitForcedCheckedCastExpr(ForcedCheckedCastExpr *E,
Expand Down Expand Up @@ -2132,6 +2133,24 @@ RValue RValueEmitter::visitExtractFunctionIsolationExpr(
return RValue(SGF, E, result);
}

RValue RValueEmitter::visitUnsafeCastExpr(UnsafeCastExpr *E, SGFContext C) {
ManagedValue original = SGF.emitRValueAsSingleValue(E->getSubExpr());
SILType resultType = SGF.getLoweredType(E->getType());

if (resultType == original.getType())
return RValue(SGF, E, original);

ManagedValue result;
if (original.getType().isAddress()) {
ASSERT(resultType.isAddress());
result = SGF.B.createUncheckedAddrCast(E, original, resultType);
} else {
result = SGF.B.createUncheckedForwardingCast(E, original, resultType);
}

return RValue(SGF, E, result);
}

RValue RValueEmitter::visitErasureExpr(ErasureExpr *E, SGFContext C) {
if (auto result = tryEmitAsBridgingConversion(SGF, E, false, C)) {
return RValue(SGF, E, *result);
Expand Down
12 changes: 12 additions & 0 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7066,6 +7066,18 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType,
}
}

// `any Sendable` -> `Any` conversion is allowed in generic
// argument positions.
{
auto erasedFromType = fromType->stripConcurrency(
/*recursive=*/true, /*dropGlobalActor=*/false);
auto erasedToType = toType->stripConcurrency(
/*recursive=*/true, /*dropGlobalActor=*/false);

if (erasedFromType->isEqual(erasedToType))
return cs.cacheType(new (ctx) UnsafeCastExpr(expr, toType));
}

auto &err = llvm::errs();
err << "fromType->getCanonicalType() = ";
fromType->getCanonicalType()->dump(err);
Expand Down
70 changes: 63 additions & 7 deletions lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,16 +904,44 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
// before pointer types could be compared.
auto locator = getLocator();
auto path = locator->getPath();
unsigned toDrop = 0;
for (const auto &elt : llvm::reverse(path)) {
if (!elt.is<LocatorPathElt::OptionalPayload>())
break;

// Disregard optional payload element to look at its source.
++toDrop;
// If there are generic types involved, we need to find
// the outermost generic types and report on them instead
// of their arguments.
// For example:
//
// <expr> -> contextual type
// -> generic type S<[Int]>
// -> generic type S<[String]>
// -> generic argument #0
//
// Is going to have from/to types as `[Int]` and `[String]` but
// the diagnostic should mention `S<[Int]>` and `S<[String]>`
// because it refers to a contextual type location.
if (locator->isLastElement<LocatorPathElt::GenericArgument>()) {
for (unsigned i = 0; i < path.size(); ++i) {
if (auto genericType = path[i].getAs<LocatorPathElt::GenericType>()) {
ASSERT(i + 1 < path.size());

fromType = resolveType(genericType->getType());
toType = resolveType(
path[i + 1].castTo<LocatorPathElt::GenericType>().getType());
break;
}
}
}

path = path.drop_back(toDrop);
while (!path.empty()) {
auto last = path.back();
if (last.is<LocatorPathElt::OptionalPayload>() ||
last.is<LocatorPathElt::GenericType>() ||
last.is<LocatorPathElt::GenericArgument>()) {
path = path.drop_back();
continue;
}

break;
}

std::optional<Diag<Type, Type>> diagnostic;
if (path.empty()) {
Expand Down Expand Up @@ -1009,6 +1037,34 @@ bool GenericArgumentsMismatchFailure::diagnoseAsError() {
break;
}

case ConstraintLocator::Member: {
auto *memberLoc = getConstraintLocator(anchor, path);
auto selectedOverload = getOverloadChoiceIfAvailable(memberLoc);
if (!selectedOverload)
return false;

auto baseTy = selectedOverload->choice.getBaseType()->getRValueType();
auto *memberRef = selectedOverload->choice.getDecl();

if (Mismatches.size() == 1) {
auto mismatchIdx = Mismatches.front();
auto actualArgTy = getActual()->getGenericArgs()[mismatchIdx];
auto requiredArgTy = getRequired()->getGenericArgs()[mismatchIdx];

emitDiagnostic(diag::types_not_equal_in_decl_ref, memberRef, baseTy,
actualArgTy, requiredArgTy);
emitDiagnosticAt(memberRef, diag::decl_declared_here, memberRef);
return true;
}

emitDiagnostic(
diag::cannot_reference_conditional_member_on_base_multiple_mismatches,
memberRef, baseTy);
emitDiagnosticAt(memberRef, diag::decl_declared_here, memberRef);
emitNotesForMismatches();
return true;
}

default:
break;
}
Expand Down
Loading

0 comments on commit 91c6425

Please sign in to comment.