Skip to content

[5.9] Diagnose attempts to reabstract variadic function types in unimplementable ways #67385

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

Merged
Merged
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
1 change: 1 addition & 0 deletions include/swift/AST/Attr.def.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ TYPE_ATTR(objc_metatype)
TYPE_ATTR(opened)
TYPE_ATTR(pack_element)
TYPE_ATTR(pseudogeneric)
TYPE_ATTR(unimplementable)
TYPE_ATTR(yields)
TYPE_ATTR(yield_once)
TYPE_ATTR(yield_many)
Expand Down
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ ERROR(c_function_pointer_from_function_with_context,none,
ERROR(objc_selector_malformed,none,"the type ObjectiveC.Selector is malformed",
())

ERROR(unsupported_variadic_function_abstraction,none,
"cannot fully abstract a value of variadic function type %0 because "
"different contexts will not be able to reliably agree on a calling "
"convention; try wrapping it in a struct",
(Type))

// Capture before declaration diagnostics.
ERROR(capture_before_declaration,none,
"closure captures %0 before it is declared", (Identifier))
Expand Down
42 changes: 33 additions & 9 deletions include/swift/AST/ExtInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,10 @@ class SILExtInfoBuilder {
// If bits are added or removed, then TypeBase::SILFunctionTypeBits
// and NumMaskBits must be updated, and they must match.

// |representation|pseudogeneric| noescape | concurrent | async |differentiability|
// | 0 .. 3 | 4 | 5 | 6 | 7 | 8 .. 10 |
// |representation|pseudogeneric| noescape | concurrent | async
// | 0 .. 3 | 4 | 5 | 6 | 7
// |differentiability|unimplementable|
// | 8 .. 10 | 11 |
//
enum : unsigned {
RepresentationMask = 0xF << 0,
Expand All @@ -662,7 +664,8 @@ class SILExtInfoBuilder {
AsyncMask = 1 << 7,
DifferentiabilityMaskOffset = 8,
DifferentiabilityMask = 0x7 << DifferentiabilityMaskOffset,
NumMaskBits = 11
UnimplementableMask = 1 << 11,
NumMaskBits = 12
};

unsigned bits; // Naturally sized for speed.
Expand All @@ -677,12 +680,13 @@ class SILExtInfoBuilder {

static constexpr unsigned makeBits(Representation rep, bool isPseudogeneric,
bool isNoEscape, bool isSendable,
bool isAsync,
bool isAsync, bool isUnimplementable,
DifferentiabilityKind diffKind) {
return ((unsigned)rep) | (isPseudogeneric ? PseudogenericMask : 0) |
(isNoEscape ? NoEscapeMask : 0) |
(isSendable ? SendableMask : 0) |
(isAsync ? AsyncMask : 0) |
(isUnimplementable ? UnimplementableMask : 0) |
(((unsigned)diffKind << DifferentiabilityMaskOffset) &
DifferentiabilityMask);
}
Expand All @@ -692,22 +696,23 @@ class SILExtInfoBuilder {
/// non-pseudogeneric, non-differentiable.
SILExtInfoBuilder()
: SILExtInfoBuilder(makeBits(SILFunctionTypeRepresentation::Thick, false,
false, false, false,
false, false, false, false,
DifferentiabilityKind::NonDifferentiable),
ClangTypeInfo(nullptr)) {}

SILExtInfoBuilder(Representation rep, bool isPseudogeneric, bool isNoEscape,
bool isSendable, bool isAsync,
bool isSendable, bool isAsync, bool isUnimplementable,
DifferentiabilityKind diffKind, const clang::Type *type)
: SILExtInfoBuilder(makeBits(rep, isPseudogeneric, isNoEscape,
isSendable, isAsync, diffKind),
isSendable, isAsync, isUnimplementable,
diffKind),
ClangTypeInfo(type)) {}

// Constructor for polymorphic type.
SILExtInfoBuilder(ASTExtInfoBuilder info, bool isPseudogeneric)
: SILExtInfoBuilder(makeBits(info.getSILRepresentation(), isPseudogeneric,
info.isNoEscape(), info.isSendable(),
info.isAsync(),
info.isAsync(), /*unimplementable*/ false,
info.getDifferentiabilityKind()),
info.getClangTypeInfo()) {}

Expand Down Expand Up @@ -746,6 +751,10 @@ class SILExtInfoBuilder {
DifferentiabilityKind::NonDifferentiable;
}

constexpr bool isUnimplementable() const {
return bits & UnimplementableMask;
}

/// Get the underlying ClangTypeInfo value.
ClangTypeInfo getClangTypeInfo() const { return clangTypeInfo; }

Expand Down Expand Up @@ -816,6 +825,12 @@ class SILExtInfoBuilder {
clangTypeInfo);
}
[[nodiscard]]
SILExtInfoBuilder withUnimplementable(bool isUnimplementable = true) const {
return SILExtInfoBuilder(isUnimplementable ? (bits | UnimplementableMask)
: (bits & ~UnimplementableMask),
clangTypeInfo);
}
[[nodiscard]]
SILExtInfoBuilder
withDifferentiabilityKind(DifferentiabilityKind differentiability) const {
return SILExtInfoBuilder(
Expand All @@ -828,6 +843,7 @@ class SILExtInfoBuilder {
return SILExtInfoBuilder(bits, ClangTypeInfo(type).getCanonical());
}


bool isEqualTo(SILExtInfoBuilder other, bool useClangTypes) const {
return bits == other.bits &&
(useClangTypes ? (clangTypeInfo == other.clangTypeInfo) : true);
Expand Down Expand Up @@ -873,7 +889,7 @@ class SILExtInfo {
/// A default ExtInfo but with a Thin convention.
static SILExtInfo getThin() {
return SILExtInfoBuilder(SILExtInfoBuilder::Representation::Thin, false,
false, false, false,
false, false, false, false,
DifferentiabilityKind::NonDifferentiable, nullptr)
.build();
}
Expand Down Expand Up @@ -901,6 +917,10 @@ class SILExtInfo {

constexpr bool isAsync() const { return builder.isAsync(); }

constexpr bool isUnimplementable() const {
return builder.isUnimplementable();
}

constexpr DifferentiabilityKind getDifferentiabilityKind() const {
return builder.getDifferentiabilityKind();
}
Expand Down Expand Up @@ -935,6 +955,10 @@ class SILExtInfo {
return builder.withAsync(isAsync).build();
}

SILExtInfo withUnimplementable(bool isUnimplementable = true) const {
return builder.withUnimplementable(isUnimplementable).build();
}

bool isEqualTo(SILExtInfo other, bool useClangTypes) const {
return builder.isEqualTo(other.builder, useClangTypes);
}
Expand Down
8 changes: 7 additions & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ class alignas(1 << TypeAlignInBits) TypeBase

protected:
enum { NumAFTExtInfoBits = 11 };
enum { NumSILExtInfoBits = 11 };
enum { NumSILExtInfoBits = 12 };

union { uint64_t OpaqueBits;

SWIFT_INLINE_BITFIELD_BASE(TypeBase, bitmax(NumTypeKindBits,8) +
Expand Down Expand Up @@ -3558,6 +3559,10 @@ class AnyFunctionType : public TypeBase {
/// replaced.
AnyFunctionType *withExtInfo(ExtInfo info) const;

bool containsPackExpansionParam() const {
return containsPackExpansionType(getParams());
}

static bool containsPackExpansionType(ArrayRef<Param> params);

static void printParams(ArrayRef<Param> Params, raw_ostream &OS,
Expand Down Expand Up @@ -4618,6 +4623,7 @@ class SILFunctionType final
}

bool isSendable() const { return getExtInfo().isSendable(); }
bool isUnimplementable() const { return getExtInfo().isUnimplementable(); }
bool isAsync() const { return getExtInfo().isAsync(); }

/// Return the array of all the yields.
Expand Down
3 changes: 2 additions & 1 deletion include/swift/SIL/AbstractionPattern.h
Original file line number Diff line number Diff line change
Expand Up @@ -1625,7 +1625,8 @@ class AbstractionPattern {
getSubstFunctionTypePattern(CanAnyFunctionType substType,
TypeConverter &TC,
AbstractionPattern coroutineYieldOrigType,
CanType coroutineYieldSubstType) const;
CanType coroutineYieldSubstType,
bool &unimplementable) const;

void dump() const LLVM_ATTRIBUTE_USED;
void print(raw_ostream &OS) const;
Expand Down
12 changes: 12 additions & 0 deletions include/swift/SIL/AbstractionPatternGenerators.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ class FunctionParamGenerator {
return origParamIsExpansion;
}

/// Return whether the current substituted parameter is a pack
/// expansion but the orig function type is opaque. This is
/// unimplementable in our ABI because the calling convention for
/// function types in the most-general abstraction pattern still
/// assumes a fixed arity.
bool isUnimplementablePackExpansion() const {
assert(!isFinished());
return (origFunctionTypeIsOpaque &&
isa<PackExpansionType>(
allSubstParams[substParamIndex].getParameterType()));
}

/// Return the substituted parameters corresponding to the current
/// orig parameter type. If the current orig parameter is not a
/// pack expansion, this will have exactly one element.
Expand Down
7 changes: 6 additions & 1 deletion lib/AST/ASTDemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,10 @@ Type ASTBuilder::createImplFunctionType(
#undef SIMPLE_CASE
}

// There's no representation of this in the mangling because it can't
// occur in well-formed programs.
bool unimplementable = false;

llvm::SmallVector<SILParameterInfo, 8> funcParams;
llvm::SmallVector<SILYieldInfo, 8> funcYields;
llvm::SmallVector<SILResultInfo, 8> funcResults;
Expand Down Expand Up @@ -610,7 +614,8 @@ Type ASTBuilder::createImplFunctionType(
}
auto einfo = SILFunctionType::ExtInfoBuilder(
representation, flags.isPseudogeneric(), !flags.isEscaping(),
flags.isSendable(), flags.isAsync(), diffKind, clangFnType)
flags.isSendable(), flags.isAsync(),
unimplementable, diffKind, clangFnType)
.build();

return SILFunctionType::get(genericSig, einfo, funcCoroutineKind,
Expand Down
3 changes: 3 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6492,6 +6492,9 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
Printer << " ";
}

if (info.isUnimplementable()) {
Printer.printSimpleAttr("@unimplementable") << " ";
}
if (info.isPseudogeneric()) {
Printer.printSimpleAttr("@pseudogeneric") << " ";
}
Expand Down
15 changes: 10 additions & 5 deletions lib/SIL/IR/AbstractionPattern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1950,9 +1950,10 @@ class SubstFunctionTypePatternVisitor
SmallVector<Type, 2> substReplacementTypes;
CanType substYieldType;
unsigned packExpansionLevel;
bool &unimplementable;

SubstFunctionTypePatternVisitor(TypeConverter &TC)
: TC(TC), packExpansionLevel(0) {}
SubstFunctionTypePatternVisitor(TypeConverter &TC, bool &unimplementable)
: TC(TC), packExpansionLevel(0), unimplementable(unimplementable) {}

// Creates and returns a fresh type parameter in the substituted generic
// signature if `pattern` is a type parameter or opaque archetype. Returns
Expand Down Expand Up @@ -2358,7 +2359,10 @@ class SubstFunctionTypePatternVisitor

pattern.forEachFunctionParam(func.getParams(), /*ignore self*/ false,
[&](FunctionParamGenerator &param) {
if (!param.isOrigPackExpansion()) {
if (param.isUnimplementablePackExpansion()) {
unimplementable = true;
// Just ignore it.
} else if (!param.isOrigPackExpansion()) {
auto newParamTy = visit(param.getSubstParams()[0].getParameterType(),
param.getOrigType());
addParam(param.getOrigFlags(), newParamTy);
Expand Down Expand Up @@ -2401,7 +2405,8 @@ std::tuple<AbstractionPattern, SubstitutionMap, AbstractionPattern>
AbstractionPattern::getSubstFunctionTypePattern(CanAnyFunctionType substType,
TypeConverter &TC,
AbstractionPattern origYieldType,
CanType substYieldType)
CanType substYieldType,
bool &unimplementable)
const {
// If this abstraction pattern isn't meaningfully generic, then we don't
// need to do any transformation.
Expand All @@ -2419,7 +2424,7 @@ const {
: AbstractionPattern::getInvalid());
}

SubstFunctionTypePatternVisitor visitor(TC);
SubstFunctionTypePatternVisitor visitor(TC, unimplementable);
auto substTy = visitor.handleUnabstractedFunctionType(substType, *this,
substYieldType,
origYieldType);
Expand Down
46 changes: 40 additions & 6 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1504,8 +1504,9 @@ class DestructureInputs {

void destructure(AbstractionPattern origType,
CanAnyFunctionType::CanParamArrayRef params,
SILExtInfoBuilder extInfoBuilder) {
visitTopLevelParams(origType, params, extInfoBuilder);
SILExtInfoBuilder extInfoBuilder,
bool &unimplementable) {
visitTopLevelParams(origType, params, extInfoBuilder, unimplementable);
}

private:
Expand All @@ -1528,7 +1529,8 @@ class DestructureInputs {
/// is still preserved under opaque abstraction
void visitTopLevelParams(AbstractionPattern origType,
CanAnyFunctionType::CanParamArrayRef params,
SILExtInfoBuilder extInfoBuilder) {
SILExtInfoBuilder extInfoBuilder,
bool &unimplementable) {
// If we're working with an opaque abstraction pattern, we never
// have to worry about pack expansions, so we can go 1-1 with the
// substituted parameters.
Expand Down Expand Up @@ -1569,6 +1571,31 @@ class DestructureInputs {
origType.forEachFunctionParam(params.drop_back(hasSelf ? 1 : 0),
/*ignore final orig param*/ hasSelf,
[&](FunctionParamGenerator &param) {
// If the parameter is unimplementable because the orig function
// type is opaque and the next substituted param is a pack
// expansion, handle that first.
if (param.isUnimplementablePackExpansion()) {
// Record that we have an unimplementable parameter; this will
// ultimately end up in the function type, and then SILGen
// is supposed to diagnose any attempt to actually emit or
// call functions of such types.
unimplementable = true;

// Also, hack up a pack parameter defensively just in case we
// *do* try to actually use or emit a function with this type.
auto substParam = param.getSubstParams()[0];
auto loweredParamTy =
TC.getLoweredRValueType(expansion, param.getOrigType(),
substParam.getParameterType());
SILPackType::ExtInfo extInfo(/*address*/ true);
auto packTy = SILPackType::get(TC.Context, extInfo, {loweredParamTy});

auto origFlags = param.getOrigFlags();
addPackParameter(packTy, origFlags.getValueOwnership(),
origFlags.isNoDerivative());
return;
}

// If the parameter is not a pack expansion, just pull off the
// next parameter and destructure it in parallel with the abstraction
// pattern for the type.
Expand Down Expand Up @@ -2132,6 +2159,8 @@ static CanSILFunctionType getSILFunctionType(
llvm::Optional<TypeConverter::GenericContextRAII> contextRAII;
if (genericSig) contextRAII.emplace(TC, genericSig);

bool unimplementable = false;

// Per above, only fully honor opaqueness in the abstraction pattern
// for thick or polymorphic functions. We don't need to worry about
// non-opaque patterns because the type-checker forbids non-thick
Expand Down Expand Up @@ -2262,7 +2291,8 @@ static CanSILFunctionType getSILFunctionType(
std::tie(origSubstPat, substFunctionTypeSubs, substYieldType)
= origType.getSubstFunctionTypePattern(substFnInterfaceType, TC,
coroutineOrigYieldType,
coroutineSubstYieldType);
coroutineSubstYieldType,
unimplementable);

// We'll lower the abstraction pattern type against itself, and then apply
// those substitutions to form the substituted lowered function type.
Expand All @@ -2289,7 +2319,7 @@ static CanSILFunctionType getSILFunctionType(
DestructureInputs destructurer(expansionContext, TC, conventions,
foreignInfo, inputs);
destructurer.destructure(origType, substFnInterfaceType.getParams(),
extInfoBuilder);
extInfoBuilder, unimplementable);
}

// Destructure the coroutine yields.
Expand Down Expand Up @@ -2344,6 +2374,7 @@ static CanSILFunctionType getSILFunctionType(
.withIsPseudogeneric(pseudogeneric)
.withConcurrent(isSendable)
.withAsync(isAsync)
.withUnimplementable(unimplementable)
.build();

return SILFunctionType::get(genericSig, silExtInfo, coroutineKind,
Expand All @@ -2370,12 +2401,15 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor(

// First compute `initialValue` input.
{
bool unimplementable = false;
ForeignInfo foreignInfo;
DestructureInputs destructurer(context, TC, conventions, foreignInfo,
inputs);
destructurer.destructure(
origType, substAccessorType.getParams(),
extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin));
extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin),
unimplementable);
assert(!unimplementable && "should never have an opaque AP here");
}

// Drop `self` parameter.
Expand Down
Loading