Sema: Rewrite witness method calls as ApplyExpr + DeclRefExpr

Special-casing these as MemberRefExprs created an asymmetry
where unbound archetype instance methods (<T : P> T.f) could
not be represented. Treating class and protocol methods
uniformly also eliminates a handful of special cases around
MemberRefExpr.

SILGen's RValue and call emission peepholes now have to know
about DeclRefExprs that point to protocol methods.

Finally, generalize the diagnostic for partially applied
mutating methods to any partially applied function with an
inout parameter, since this is not supported.

Fixes <rdar://problem/20564672>.

Swift SVN r29298
This commit is contained in:
Slava Pestov
2015-06-04 15:57:58 +00:00
parent 93881c9535
commit 7319a97ab4
10 changed files with 330 additions and 194 deletions

View File

@@ -1548,9 +1548,11 @@ ERROR(ambiguous_decl_ref,tce_sema,none,
ERROR(ambiguous_operator_ref,tce_sema,none,
"ambiguous use of operator %0", (Identifier))
ERROR(partial_application_of_method_invalid,tce_sema,none,
"partial application of 'mutating' %select{struct method|enum method|"
"generic method|protocol method}0 is not allowed",
// Cannot capture inout-ness of a parameter
// Partial application of foreign functions not supported
ERROR(partial_application_of_function_invalid,tce_sema,none,
"partial application of %select{function with 'inout' parameters|"
"'mutating' method|method in @objc protocol}0 is not allowed",
(unsigned))
ERROR(self_assignment_var,tce_sema,none,

View File

@@ -708,7 +708,7 @@ namespace {
static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc,
SILDeclRef constant,
ArgumentSource &selfValue,
CanAnyFunctionType substAccessorType,
CanAnyFunctionType substFnType,
ArrayRef<Substitution> &substitutions) {
auto fd = cast<AbstractFunctionDecl>(constant.getDecl());
auto protocol = cast<ProtocolDecl>(fd->getDeclContext());
@@ -798,8 +798,11 @@ static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc,
materializeSelfIfNecessary();
// The protocol self is implicitly decurried.
substFnType = cast<AnyFunctionType>(substFnType.getResult());
return Callee::forArchetype(gen, openingSite, selfTy,
constant, substAccessorType, loc);
constant, substFnType, loc);
}
/// An ASTVisitor for building SIL function calls.
@@ -915,7 +918,8 @@ public:
for (auto callSite : CallSites) {
addSite(callSite, false);
}
// The self application might be a MemberRefExpr if "self" is an archetype.
// The self application might be a DynamicMemberRefExpr.
if (auto selfApply = dyn_cast_or_null<ApplyExpr>(SelfApplyExpr)) {
addSite(selfApply, otherCtorRefUsesAllocating);
}
@@ -1022,6 +1026,68 @@ public:
bool requiresAllocRefDynamic = false;
// Determine whether the method is dynamically dispatched.
if (auto *proto = dyn_cast<ProtocolDecl>(afd->getDeclContext())) {
// We have four cases to deal with here:
//
// 1) for a "static" / "type" method, the base is a metatype.
// 2) for a classbound protocol, the base is a class-bound protocol rvalue,
// which is loadable.
// 3) for a mutating method, the base has inout type.
// 4) for a nonmutating method, the base is a general archetype
// rvalue, which is address-only. The base is passed at +0, so it isn't
// consumed.
//
// In the last case, the AST has this call typed as being applied
// to an rvalue, but the witness is actually expecting a pointer
// to the +0 value in memory. We just pass in the address since
// archetypes are address-only.
CanAnyFunctionType substFnType = getSubstFnType();
assert(!CallSites.empty());
ApplyExpr *thisCallSite = CallSites.back();
CallSites.pop_back();
ArgumentSource selfValue = thisCallSite->getArg();
ArrayRef<Substitution> subs = e->getDeclRef().getSubstitutions();
SILDeclRef::Kind kind = SILDeclRef::Kind::Func;
if (isa<ConstructorDecl>(afd)) {
if (proto->isObjC()) {
SILLocation loc = thisCallSite->getArg();
// For Objective-C initializers, we only have an initializing
// initializer. We need to allocate the object ourselves.
kind = SILDeclRef::Kind::Initializer;
auto metatype = std::move(selfValue).getAsSingleValue(SGF);
auto allocated = allocateObjCObject(metatype, loc);
auto allocatedType = allocated.getType().getSwiftRValueType();
selfValue = ArgumentSource(loc, RValue(allocated, allocatedType));
} else {
// For non-Objective-C initializers, we have an allocating
// initializer to call.
kind = SILDeclRef::Kind::Allocator;
}
}
SILDeclRef constant = SILDeclRef(afd, kind);
// Prepare the callee. This can modify both selfValue and subs.
Callee theCallee = prepareArchetypeCallee(SGF, e, constant, selfValue,
substFnType, subs);
setSelfParam(std::move(selfValue), thisCallSite);
setCallee(std::move(theCallee));
// If there are substitutions, add them now.
if (!subs.empty()) {
ApplyCallee->setSubstitutions(SGF, e, subs, CallDepth);
}
return;
}
if (e->getAccessSemantics() != AccessSemantics::Ordinary) {
isDynamicallyDispatched = false;
} else {
@@ -1222,78 +1288,6 @@ public:
return subs.slice(innerIdx);
}
void visitMemberRefExpr(MemberRefExpr *e) {
auto *fd = dyn_cast<AbstractFunctionDecl>(e->getMember().getDecl());
// If the base is a non-protocol, non-archetype type, then this is a load of
// a function pointer out of a vardecl. Just emit it as an rvalue.
if (!fd)
return visitExpr(e);
// We have four cases to deal with here:
//
// 1) for a "static" / "type" method, the base is a metatype.
// 2) for a classbound protocol, the base is a class-bound protocol rvalue,
// which is loadable.
// 3) for a mutating method, the base has inout type.
// 4) for a nonmutating method, the base is a general archetype
// rvalue, which is address-only. The base is passed at +0, so it isn't
// consumed.
//
// In the last case, the AST has this call typed as being applied
// to an rvalue, but the witness is actually expecting a pointer
// to the +0 value in memory. We just pass in the address since
// archetypes are address-only.
ArgumentSource selfValue = e->getBase();
auto *proto = cast<ProtocolDecl>(fd->getDeclContext());
ArrayRef<Substitution> subs = e->getMember().getSubstitutions();
// Figure out the kind of declaration reference we're working with.
SILDeclRef::Kind kind = SILDeclRef::Kind::Func;
if (isa<ConstructorDecl>(fd)) {
if (proto->isObjC()) {
// For Objective-C initializers, we only have an initializing
// initializer. We need to allocate the object ourselves.
kind = SILDeclRef::Kind::Initializer;
auto metatype = std::move(selfValue).getAsSingleValue(SGF);
auto allocated = allocateObjCObject(metatype, e->getBase());
auto allocatedType = allocated.getType().getSwiftRValueType();
selfValue = ArgumentSource(e->getBase(),
RValue(allocated, allocatedType));
} else {
// For non-Objective-C initializers, we have an allocating
// initializer to call.
kind = SILDeclRef::Kind::Allocator;
}
}
SILDeclRef constant = SILDeclRef(fd, kind);
// Gross. We won't have any call sites if this is a curried generic method
// application, because it doesn't look like a call site in the AST, but a
// member ref.
CanFunctionType substFnType;
if (!CallSites.empty() || dyn_cast_or_null<ApplyExpr>(SelfApplyExpr))
substFnType = getSubstFnType();
else
substFnType = cast<FunctionType>(e->getType()->getCanonicalType());
// Prepare the callee. This can modify both selfValue and subs.
Callee theCallee = prepareArchetypeCallee(SGF, e, constant, selfValue,
substFnType, subs);
setSelfParam(std::move(selfValue), e);
setCallee(std::move(theCallee));
// If there are substitutions, add them now.
if (!subs.empty()) {
ApplyCallee->setSubstitutions(SGF, e, subs, CallDepth);
}
}
void visitFunctionConversionExpr(FunctionConversionExpr *e) {
// FIXME: Check whether this function conversion requires us to build a
// thunk.
@@ -3451,9 +3445,6 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &gen,
assert(!isDirectUse && "direct use of protocol accessor?");
assert(!isSuper && "super call to protocol method?");
// The protocol self is implicitly decurried.
substAccessorType = cast<AnyFunctionType>(substAccessorType.getResult());
return prepareArchetypeCallee(gen, loc, constant, selfValue,
substAccessorType, substitutions);
}

View File

@@ -1804,13 +1804,6 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *E, SGFContext C) {
return RValue(SGF, E, ManagedValue::forUnmanaged(MT));
}
if (isa<AbstractFunctionDecl>(E->getMember().getDecl())) {
// Method references into generics are represented as member refs instead
// of apply exprs for some reason. Send this down the correct path to be
// treated as a curried method application.
return SGF.emitApplyExpr(E, C);
}
// If we have a nominal type decl as our base, try to use special logic to
// emit the base rvalue's member using special logic that will let us avoid
// extra retains and releases.

View File

@@ -472,7 +472,7 @@ namespace {
locator, locator, implicit, semantics,
/*isDynamic=*/false);
if (!result)
return nullptr;;
return nullptr;
return result;
}
@@ -967,11 +967,11 @@ namespace {
return ref;
}
// For types, properties, and members of archetypes, build
// member references.
if (isDependentConformingRef || isa<TypeDecl>(member) ||
isa<VarDecl>(member)) {
// For types and properties, build member references.
if (isa<TypeDecl>(member) || isa<VarDecl>(member)) {
assert(!dynamicSelfFnType && "Converted type doesn't make sense here");
assert(baseIsInstance || !member->isInstanceMember());
auto memberRefExpr
= new (context) MemberRefExpr(base, dotLoc, memberRef,
memberLoc, Implicit, semantics);
@@ -2227,6 +2227,8 @@ namespace {
choice.getReasonUnavailable(cs));
}
recordUnsupportedPartialApply(expr, newRef);
return newRef;
}
@@ -2361,6 +2363,9 @@ namespace {
expr->isImplicit(),
AccessSemantics::Ordinary);
if (auto newDeclRef = dyn_cast<DeclRefExpr>(newRef))
recordUnsupportedPartialApply(newDeclRef, newDeclRef);
if (choice.isPotentiallyUnavailable()) {
newRef = convertUnavailableToOptional(newRef, decl, expr->getLoc(),
choice.getReasonUnavailable(cs));
@@ -2488,23 +2493,22 @@ namespace {
}
private:
struct MemberPartialApplication {
unsigned level : 29;
// Selector for the partial_application_of_method_invalid diagnostic
// Selector for the partial_application_of_function_invalid diagnostic
// message.
struct PartialApplication {
unsigned level : 29;
enum : unsigned {
Struct,
Enum,
Archetype,
Protocol
Function,
MutatingMethod,
ObjCProtocolMethod
};
unsigned kind : 3;
};
// A map used to track partial applications of methods to require that they
// be fully applied. Partial applications of value types would capture
// 'self' as an inout and hide any mutation of 'self', which is surprising.
llvm::SmallDenseMap<Expr*, MemberPartialApplication, 2>
// Partial applications of functions with inout parameters is not permitted,
// so we track them to ensure they are fully applied. The map value is the
// number of remaining applications.
llvm::SmallDenseMap<Expr*, PartialApplication, 2>
InvalidPartialApplications;
/// A list of "suspicious" optional injections that come from
@@ -2576,48 +2580,9 @@ namespace {
isDynamic,
reason);
// If this is an application of a mutating method,
// arrange for us to check that it gets fully applied.
FuncDecl *fn = nullptr;
Optional<unsigned> kind;
if (auto apply = dyn_cast<ApplyExpr>(member)) {
auto selfInoutTy = apply->getArg()->getType()->getAs<InOutType>();
if (!selfInoutTy)
goto not_mutating_member;
auto selfTy = selfInoutTy->getObjectType();
auto fnDeclRef = dyn_cast<DeclRefExpr>(apply->getFn());
if (!fnDeclRef)
goto not_mutating_member;
fn = dyn_cast<FuncDecl>(fnDeclRef->getDecl());
if (selfTy->getStructOrBoundGenericStruct())
kind = MemberPartialApplication::Struct;
else if (selfTy->getEnumOrBoundGenericEnum())
kind = MemberPartialApplication::Enum;
else
goto not_mutating_member;
} else if (auto pmRef = dyn_cast<MemberRefExpr>(member)) {
auto baseInoutTy = pmRef->getBase()->getType()->getAs<InOutType>();
if (!baseInoutTy)
goto not_mutating_member;
auto baseTy = baseInoutTy->getObjectType();
if (baseTy->getRValueType()->isOpenedExistential()) {
kind = MemberPartialApplication::Protocol;
} else if (baseTy->hasReferenceSemantics()) {
goto not_mutating_member;
} else if (isa<FuncDecl>(pmRef->getMember().getDecl()))
kind = MemberPartialApplication::Archetype;
else
goto not_mutating_member;
fn = dyn_cast<FuncDecl>(pmRef->getMember().getDecl());
}
if (auto applyExpr = dyn_cast<ApplyExpr>(member))
advancePartialApplyExpr(applyExpr, applyExpr);
if (fn && fn->isInstanceMember())
InvalidPartialApplications.insert({
member,
// We need to apply all of the non-self argument clauses.
{fn->getNaturalArgumentCount() - 1, kind.getValue() },
});
not_mutating_member:
return member;
}
@@ -2884,28 +2849,106 @@ namespace {
llvm_unreachable("Already type-checked");
}
/// If this is an application of a function that cannot be partially
/// applied, arrange for us to check that it gets fully applied.
void recordUnsupportedPartialApply(DeclRefExpr *expr, Expr *fnExpr) {
bool requiresFullApply = false;
unsigned kind;
auto fn = dyn_cast<FuncDecl>(expr->getDecl());
if (!fn)
return;
// @objc protocol methods cannot be partially applied.
if (auto proto = fn->getDeclContext()->isProtocolOrProtocolExtensionContext()) {
if (proto->isObjC()) {
requiresFullApply = true;
kind = PartialApplication::ObjCProtocolMethod;
}
}
if (requiresFullApply) {
// We need to apply all argument clauses.
InvalidPartialApplications.insert({
fnExpr, {fn->getNaturalArgumentCount(), kind}
});
}
}
/// If this is an application of a function that cannot be partially
/// applied, arrange for us to check that it gets fully applied.
void recordUnsupportedPartialApply(ApplyExpr *expr, Expr *fnExpr) {
bool requiresFullApply = false;
unsigned kind;
auto fnDeclRef = dyn_cast<DeclRefExpr>(fnExpr);
if (!fnDeclRef)
return;
recordUnsupportedPartialApply(fnDeclRef, fnExpr);
auto fn = dyn_cast<FuncDecl>(fnDeclRef->getDecl());
if (!fn)
return;
if (fn->isInstanceMember())
kind = PartialApplication::MutatingMethod;
else
kind = PartialApplication::Function;
// Functions with inout parameters cannot be partially applied.
auto argTy = expr->getArg()->getType();
if (auto tupleTy = argTy->getAs<TupleType>()) {
for (auto eltTy : tupleTy->getElementTypes()) {
if (eltTy->getAs<InOutType>()) {
requiresFullApply = true;
break;
}
}
} else if (argTy->getAs<InOutType>()) {
requiresFullApply = true;
}
if (requiresFullApply) {
// We need to apply all argument clauses.
InvalidPartialApplications.insert({
fnExpr, {fn->getNaturalArgumentCount(), kind}
});
}
}
/// See if this application advanced a partial value type application.
void advancePartialApplyExpr(ApplyExpr *expr, Expr *result) {
Expr *fnExpr = expr->getFn()->getSemanticsProvidingExpr();
if (auto forceExpr = dyn_cast<ForceValueExpr>(fnExpr))
fnExpr = forceExpr->getSubExpr()->getSemanticsProvidingExpr();
if (auto dotSyntaxExpr = dyn_cast<DotSyntaxBaseIgnoredExpr>(fnExpr))
fnExpr = dotSyntaxExpr->getRHS();
recordUnsupportedPartialApply(expr, fnExpr);
auto foundApplication = InvalidPartialApplications.find(fnExpr);
if (foundApplication == InvalidPartialApplications.end())
return;
unsigned level = foundApplication->second.level;
assert(level > 0);
InvalidPartialApplications.erase(foundApplication);
if (level > 1) {
// We have remaining argument clauses.
InvalidPartialApplications.insert({
result, {level - 1, foundApplication->second.kind}
});
}
}
Expr *visitApplyExpr(ApplyExpr *expr) {
auto result = finishApply(expr, expr->getType(),
ConstraintLocatorBuilder(
cs.getConstraintLocator(expr)));
// See if this application advanced a partial value type application.
Expr *fn = expr->getFn()->getSemanticsProvidingExpr();
if (auto force = dyn_cast<ForceValueExpr>(fn))
fn = force->getSubExpr()->getSemanticsProvidingExpr();
auto foundApplication = InvalidPartialApplications.find(fn);
if (foundApplication != InvalidPartialApplications.end()) {
unsigned level = foundApplication->second.level;
assert(level > 0);
InvalidPartialApplications.erase(foundApplication);
if (level > 1)
InvalidPartialApplications.insert({
result, {
level - 1,
foundApplication->second.kind
}
});
}
// Record a partial value type application, if necessary.
advancePartialApplyExpr(expr, result);
return result;
}
@@ -3412,7 +3455,7 @@ namespace {
for (auto &unapplied : InvalidPartialApplications) {
unsigned kind = unapplied.second.kind;
tc.diagnose(unapplied.first->getLoc(),
diag::partial_application_of_method_invalid,
diag::partial_application_of_function_invalid,
kind);
}

View File

@@ -1010,14 +1010,7 @@ void ConstraintSystem::openGeneric(
/// declared.
static void addSelfConstraint(ConstraintSystem &cs, Type objectTy, Type selfTy,
ConstraintLocatorBuilder locator){
// When referencing a protocol member, we need the object type to be usable
// as the Self type of the protocol, which covers anything that conforms to
// the protocol as well as existentials that include that protocol.
if (selfTy->is<ProtocolType>()) {
cs.addConstraint(ConstraintKind::SelfObjectOfProtocol, objectTy, selfTy,
cs.getConstraintLocator(locator));
return;
}
assert(!selfTy->is<ProtocolType>());
// Otherwise, use a subtype constraint for classes to cope with inheritance.
if (selfTy->getClassOrBoundGenericClass()) {

View File

@@ -126,23 +126,16 @@ protocol P {
mutating func mut(x: Int)
}
@objc
protocol P2 {
func bar(x: Int)
}
func generic<T: P>(var t: T) {
_ = t.bar
_ = t.mut // expected-error{{partial application of 'mutating' generic method is not allowed}}
_ = t.mut // expected-error{{partial application of 'mutating' method is not allowed}}
var _ : () = t.bar(0)
}
func existential(var p: P, p2 : P2) {
func existential(var p: P) {
p.mut(1)
_ = p.bar
_ = p.mut // expected-error{{partial application of 'mutating' protocol method is not allowed}}
_ = p2.bar
_ = p.mut // expected-error{{partial application of 'mutating' method is not allowed}}
var _ : () = p.bar(0)
}

View File

@@ -0,0 +1,20 @@
// RUN: %target-parse-verify-swift
import Swift
@objc
protocol P2 {
func bar(x: Int)
}
func existential(p2 : P2) {
_ = p2.bar // expected-error{{partial application of method in @objc protocol is not allowed}}
}
func archetype<T: P2>(p2 : T) {
_ = p2.bar // expected-error{{partial application of method in @objc protocol is not allowed}}
}
func archetypeMeta<T: P2>(p2 : T) {
_ = T.bar // FIXME: not yet diagnosed
}

View File

@@ -22,3 +22,46 @@ foo(S(offset: 4))
let p : P = S(offset: 4)
print("\(p.scale(5)(y: 7))\n", appendNewline: false)
// CHECK: 39
func culDeSac<T>(t: T) {}
func roundabout<T>(t: T, _ u: T -> ()) { u(t) }
protocol Proto {
typealias Category
func f(t: Self)(i: Category, j: Category)
static func ff()(i: Category, j: Category)
}
class Q : Proto {
typealias Category = Int
var q: Int
init(q: Int) { self.q = q }
func f(t: Q)(i: Int, j: Int) { print(q * t.q + i * j) }
static func ff()(i: Int, j: Int) { print(i * j) }
}
func suburbanJungle<T : Proto where T.Category == Int>(t: T) {
roundabout(T.ff) { $0()(i: 2, j: 10) }
roundabout(T.ff()) { $0(i: 4, j: 10) }
culDeSac(T.ff()(i: 6, j: 10))
roundabout(T.f) { $0(t)(t)(i: 1, j: 2) }
roundabout(T.f(t)) { $0(t)(i: 3, j: 4) }
roundabout(T.f(t)(t)) { $0(i: 5, j: 6) }
culDeSac(T.f(t)(t)(i: 7, j: 8))
roundabout(t.f) { $0(t)(i: 9, j: 10) }
roundabout(t.f(t)) { $0(i: 11, j: 12) }
culDeSac(t.f(t)(i: 13, j: 14))
}
suburbanJungle(Q(q: 3))
// CHECK: 20
// CHECK: 40
// CHECK: 60
// CHECK: 11
// CHECK: 21
// CHECK: 39
// CHECK: 65
// CHECK: 99
// CHECK: 141
// CHECK: 191

View File

@@ -1,15 +1,64 @@
// RUN: %target-swift-frontend -emit-silgen %s > /tmp/x.sil
// RUN: %target-swift-frontend -emit-silgen %s | FileCheck %s
protocol Foo {
static func foo()
static func staticFunc()
func instanceFunc()
}
func getFoo<T: Foo>(t: T.Type) -> () -> () {
return t.foo
// CHECK-LABEL: sil hidden @_TF21partial_apply_generic14getStaticFunc1uRq_S_3Foo_FMq_FT_T_
func getStaticFunc1<T: Foo>(t: T.Type) -> () -> () {
// CHECK: [[REF:%.*]] = function_ref @_TZFP21partial_apply_generic3Foo10staticFuncuRq_S0__FMq_FT_T_
// CHECK-NEXT: apply [[REF]]<T>(%0)
return t.staticFunc
// CHECK-NEXT: return
}
// CHECK-LABEL: sil hidden @_TF21partial_apply_generic6getFoouRq_S_3Foo_FMq_FT_T_
// CHECK: function_ref @_TZFP21partial_apply_generic3Foo3foouRq_S0__FMq_FT_T_
// CHECK-LABEL: sil shared @_TZFP21partial_apply_generic3Foo10staticFuncuRq_S0__FMq_FT_T_
// CHECK: [[REF:%.*]] = witness_method $Self, #Foo.staticFunc!1
// CHECK-NEXT: partial_apply [[REF]]<Self>(%0)
// CHECK-NEXT: return
// CHECK-LABEL: sil hidden @_TF21partial_apply_generic14getStaticFunc2uRq_S_3Foo_Fq_FT_T_
func getStaticFunc2<T: Foo>(t: T) -> () -> () {
// CHECK: [[REF:%.*]] = function_ref @_TZFP21partial_apply_generic3Foo10staticFuncuRq_S0__FMq_FT_T_
// CHECK: apply [[REF]]<T>
return T.staticFunc
// CHECK-NEXT: destroy_addr %0 : $*T
// CHECK-NEXT: return
}
// CHECK-LABEL: sil hidden @_TF21partial_apply_generic16getInstanceFunc1uRq_S_3Foo_Fq_FT_T_
func getInstanceFunc1<T: Foo>(t: T) -> () -> () {
// CHECK: [[REF:%.*]] = function_ref @_TFP21partial_apply_generic3Foo12instanceFuncuRq_S0__Fq_FT_T_
// CHECK-NEXT: alloc_stack $T
// CHECK-NEXT: copy_addr %0 to [initialization]
// CHECK-NEXT: apply [[REF]]<T>
return t.instanceFunc
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: destroy_addr %0 : $*T
// CHECK-NEXT: return
}
// CHECK-LABEL: sil shared @_TFP21partial_apply_generic3Foo12instanceFuncuRq_S0__Fq_FT_T_
// CHECK: [[REF:%.*]] = witness_method $Self, #Foo.instanceFunc!1
// CHECK-NEXT: partial_apply [[REF]]<Self>(%0)
// CHECK-NEXT: return
// CHECK-LABEL: sil hidden @_TF21partial_apply_generic16getInstanceFunc2uRq_S_3Foo_Fq_Fq_FT_T_
func getInstanceFunc2<T: Foo>(t: T) -> (T) -> () -> () {
// CHECK: [[REF:%.*]] = function_ref @_TFP21partial_apply_generic3Foo12instanceFuncuRq_S0__Fq_FT_T_
// CHECK-NEXT: partial_apply [[REF]]<T>(
return T.instanceFunc
// CHECK-NEXT: destroy_addr %0 : $*
// CHECK-NEXT: return
}
// CHECK-LABEL: sil hidden @_TF21partial_apply_generic16getInstanceFunc3uRq_S_3Foo_FMq_Fq_FT_T_
func getInstanceFunc3<T: Foo>(t: T.Type) -> (T) -> () -> () {
// CHECK: [[REF:%.*]] = function_ref @_TFP21partial_apply_generic3Foo12instanceFuncuRq_S0__Fq_FT_T_
// CHECK-NEXT: partial_apply [[REF]]<T>(
return t.instanceFunc
// CHECK-NEXT: return
}
// CHECK-LABEL: sil shared @_TZFP21partial_apply_generic3Foo3foouRq_S0__FMq_FT_T_
// CHECK: witness_method $Self, #Foo.foo!1

View File

@@ -6,3 +6,12 @@ func foo(inout x: Int) {
return x
}
}
// But not partially applied.
func curriedFoo(inout x: Int)(y: Int) -> Int {
return x + y
}
var score: Int = 0
_ = curriedFoo(&score) // expected-error {{partial application of function with 'inout' parameters is not allowed}}