[NCGenerics] fix TypeLowering

This commit is contained in:
Kavon Farvardin
2023-12-15 16:55:49 -08:00
parent d80719827c
commit 34f359d8d0
3 changed files with 91 additions and 32 deletions

View File

@@ -1671,8 +1671,10 @@ ProtocolConformanceRef ModuleDecl::lookupConformance(Type type,
if (auto nominal = type->getAnyNominal()) { if (auto nominal = type->getAnyNominal()) {
ImplicitKnownProtocolConformanceRequest icvRequest{nominal, *kp}; ImplicitKnownProtocolConformanceRequest icvRequest{nominal, *kp};
if (getASTContext().evaluator.hasActiveRequest(icvRequest) || if (getASTContext().evaluator.hasActiveRequest(icvRequest) ||
getASTContext().evaluator.hasActiveRequest(request)) getASTContext().evaluator.hasActiveRequest(request)) {
assert(!getInvertibleProtocolKind(*kp));
return ProtocolConformanceRef::forInvalid(); return ProtocolConformanceRef::forInvalid();
}
} }
} }
@@ -1731,13 +1733,35 @@ static ProtocolConformanceRef getBuiltinTupleTypeConformance(
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
} }
using EitherFunctionType =
llvm::PointerUnion<const SILFunctionType *, const FunctionType *>;
/// Whether the given function type conforms to Sendable. /// Whether the given function type conforms to Sendable.
static bool isSendableFunctionType(const FunctionType *functionType) { static bool isSendableFunctionType(EitherFunctionType eitherFnTy) {
if (functionType->isSendable()) FunctionTypeRepresentation representation;
return true;
if (auto silFnTy = eitherFnTy.dyn_cast<const SILFunctionType *>()) {
if (silFnTy->isSendable())
return true;
// convert SILFunctionTypeRepresentation -> FunctionTypeRepresentation
auto converted = convertRepresentation(silFnTy->getRepresentation());
if (!converted)
return false;
representation = *converted;
} else {
auto functionType = eitherFnTy.get<const FunctionType *>();
if (functionType->isSendable())
return true;
representation = functionType->getExtInfo().getRepresentation();
}
// C and thin function types have no captures, so they are Sendable. // C and thin function types have no captures, so they are Sendable.
switch (functionType->getExtInfo().getRepresentation()) { switch (representation) {
case FunctionTypeRepresentation::Block: case FunctionTypeRepresentation::Block:
case FunctionTypeRepresentation::Swift: case FunctionTypeRepresentation::Swift:
return false; return false;
@@ -1749,41 +1773,47 @@ static bool isSendableFunctionType(const FunctionType *functionType) {
} }
/// Whether the given function type conforms to Escapable. /// Whether the given function type conforms to Escapable.
static bool isEscapableFunctionType(const FunctionType *functionType) { static bool isEscapableFunctionType(EitherFunctionType eitherFnTy) {
if (functionType->isNoEscape()) if (auto silFnTy = eitherFnTy.dyn_cast<const SILFunctionType *>()) {
return false; return !silFnTy->isNoEscape();
}
// FIXME: do we need to also include autoclosures?? auto functionType = eitherFnTy.get<const FunctionType *>();
return true; // TODO: what about autoclosures?
return !functionType->isNoEscape();
} }
/// Synthesize a builtin function type conformance to the given protocol, if /// Synthesize a builtin function type conformance to the given protocol, if
/// appropriate. /// appropriate.
static ProtocolConformanceRef getBuiltinFunctionTypeConformance( static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
Type type, const FunctionType *functionType, ProtocolDecl *protocol) { Type type, EitherFunctionType functionType, ProtocolDecl *protocol) {
ASTContext &ctx = protocol->getASTContext(); ASTContext &ctx = protocol->getASTContext();
// @Sendable function types are Sendable.
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
isSendableFunctionType(functionType)) {
return ProtocolConformanceRef(
ctx.getBuiltinConformance(type, protocol,
BuiltinConformanceKind::Synthesized));
}
// Functions cannot permanently destroy a move-only var/let auto synthesizeConformance = [&]() -> ProtocolConformanceRef {
// that they capture, so it's safe to copy functions, like classes.
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
return ProtocolConformanceRef( return ProtocolConformanceRef(
ctx.getBuiltinConformance(type, protocol, ctx.getBuiltinConformance(type, protocol,
BuiltinConformanceKind::Synthesized)); BuiltinConformanceKind::Synthesized));
} };
if (protocol->isSpecificProtocol(KnownProtocolKind::Escapable) && if (auto kp = protocol->getKnownProtocolKind()) {
isEscapableFunctionType(functionType)) { switch (*kp) {
return ProtocolConformanceRef( case KnownProtocolKind::Escapable:
ctx.getBuiltinConformance(type, protocol, if (isEscapableFunctionType(functionType))
BuiltinConformanceKind::Synthesized)); return synthesizeConformance();
break;
case KnownProtocolKind::Sendable:
// @Sendable function types are Sendable.
if (isSendableFunctionType(functionType))
return synthesizeConformance();
break;
case KnownProtocolKind::Copyable:
// Functions cannot permanently destroy a move-only var/let
// that they capture, so it's safe to copy functions, like classes.
return synthesizeConformance();
default:
break;
}
} }
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol); return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
@@ -1959,6 +1989,11 @@ LookupConformanceInModuleRequest::evaluate(
return getBuiltinFunctionTypeConformance(type, functionType, protocol); return getBuiltinFunctionTypeConformance(type, functionType, protocol);
} }
// SIL function types in the AST can conform to protocols
if (auto silFn = type->getAs<SILFunctionType>()) {
return getBuiltinFunctionTypeConformance(type, silFn, protocol);
}
// Metatypes can conform to protocols. // Metatypes can conform to protocols.
if (auto metatypeType = type->getAs<AnyMetatypeType>()) { if (auto metatypeType = type->getAs<AnyMetatypeType>()) {
return getBuiltinMetaTypeTypeConformance(type, metatypeType, protocol); return getBuiltinMetaTypeTypeConformance(type, metatypeType, protocol);
@@ -1969,6 +2004,18 @@ LookupConformanceInModuleRequest::evaluate(
return getBuiltinBuiltinTypeConformance(type, builtinType, protocol); return getBuiltinBuiltinTypeConformance(type, builtinType, protocol);
} }
#ifndef NDEBUG
// Ensure we haven't missed queries for the specialty SIL types
// in the AST in conformance to one of the invertible protocols.
if (auto kp = protocol->getKnownProtocolKind())
if (getInvertibleProtocolKind(*kp))
assert(!(type->is<SILFunctionType,
SILBoxType,
SILMoveOnlyWrappedType,
SILPackType,
SILTokenType>()));
#endif
auto nominal = type->getAnyNominal(); auto nominal = type->getAnyNominal();
// If we don't have a nominal type, there are no conformances. // If we don't have a nominal type, there are no conformances.

View File

@@ -116,15 +116,16 @@ CaptureKind TypeConverter::getDeclCaptureKind(CapturedValue capture,
assert(var->hasStorage() && assert(var->hasStorage() &&
"should not have attempted to directly capture this variable"); "should not have attempted to directly capture this variable");
auto contextTy = var->getTypeInContext();
auto &lowering = getTypeLowering( auto &lowering = getTypeLowering(
var->getTypeInContext(), TypeExpansionContext::noOpaqueTypeArchetypesSubstitution( contextTy, TypeExpansionContext::noOpaqueTypeArchetypesSubstitution(
expansion.getResilienceExpansion())); expansion.getResilienceExpansion()));
// If this is a noncopyable 'let' constant that is not a shared paramdecl or // If this is a noncopyable 'let' constant that is not a shared paramdecl or
// used by a noescape capture, then we know it is boxed and want to pass it in // used by a noescape capture, then we know it is boxed and want to pass it in
// its boxed form so we can obey Swift's capture reference semantics. // its boxed form so we can obey Swift's capture reference semantics.
if (!var->supportsMutation() if (!var->supportsMutation()
&& lowering.getLoweredType().getASTType()->isNoncopyable() && contextTy->isNoncopyable()
&& !capture.isNoEscape()) { && !capture.isNoEscape()) {
auto *param = dyn_cast<ParamDecl>(var); auto *param = dyn_cast<ParamDecl>(var);
if (!param || (param->getValueOwnership() != ValueOwnership::Shared && if (!param || (param->getValueOwnership() != ValueOwnership::Shared &&

View File

@@ -159,17 +159,28 @@ static void tryEmitContainmentFixits(InFlightDiagnostic &&diag,
/// MARK: conformance queries /// MARK: conformance queries
static bool conformsToInvertible(CanType type, InvertibleProtocolKind ip) { static bool conformsToInvertible(CanType type, InvertibleProtocolKind ip) {
assert(!type->hasTypeParameter() && "forgot to mapTypeIntoContext first");
auto &ctx = type->getASTContext(); auto &ctx = type->getASTContext();
auto *invertible = ctx.getProtocol(getKnownProtocolKind(ip));
assert(invertible);
// Pack expansions such as `repeat T` themselves do not have conformances, // Pack expansions such as `repeat T` themselves do not have conformances,
// so check its pattern type for conformance. // so check its pattern type for conformance.
if (auto *pet = type->getAs<PackExpansionType>()) { if (auto *pet = type->getAs<PackExpansionType>()) {
type = pet->getPatternType()->getCanonicalType(); type = pet->getPatternType()->getCanonicalType();
} }
auto *invertible = ctx.getProtocol(getKnownProtocolKind(ip)); // Must either not have a type parameter, or in the case of a
assert(invertible); // BoundGenericXType, have a nominal available.
assert(!type->hasTypeParameter() || type.getAnyNominal()
&& "caller forgot to mapTypeIntoContext!");
// The SIL types in the AST do not have real conformances, and should have
// been handled earlier.
assert(!(type->is<SILBoxType,
SILMoveOnlyWrappedType,
SILPackType,
SILTokenType>()));
const bool conforms = const bool conforms =
(bool)TypeChecker::conformsToProtocol(type, (bool)TypeChecker::conformsToProtocol(type,