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, ERROR(ambiguous_operator_ref,tce_sema,none,
"ambiguous use of operator %0", (Identifier)) "ambiguous use of operator %0", (Identifier))
ERROR(partial_application_of_method_invalid,tce_sema,none, // Cannot capture inout-ness of a parameter
"partial application of 'mutating' %select{struct method|enum method|" // Partial application of foreign functions not supported
"generic method|protocol method}0 is not allowed", 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)) (unsigned))
ERROR(self_assignment_var,tce_sema,none, ERROR(self_assignment_var,tce_sema,none,

View File

@@ -708,7 +708,7 @@ namespace {
static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc, static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc,
SILDeclRef constant, SILDeclRef constant,
ArgumentSource &selfValue, ArgumentSource &selfValue,
CanAnyFunctionType substAccessorType, CanAnyFunctionType substFnType,
ArrayRef<Substitution> &substitutions) { ArrayRef<Substitution> &substitutions) {
auto fd = cast<AbstractFunctionDecl>(constant.getDecl()); auto fd = cast<AbstractFunctionDecl>(constant.getDecl());
auto protocol = cast<ProtocolDecl>(fd->getDeclContext()); auto protocol = cast<ProtocolDecl>(fd->getDeclContext());
@@ -798,8 +798,11 @@ static Callee prepareArchetypeCallee(SILGenFunction &gen, SILLocation loc,
materializeSelfIfNecessary(); materializeSelfIfNecessary();
// The protocol self is implicitly decurried.
substFnType = cast<AnyFunctionType>(substFnType.getResult());
return Callee::forArchetype(gen, openingSite, selfTy, return Callee::forArchetype(gen, openingSite, selfTy,
constant, substAccessorType, loc); constant, substFnType, loc);
} }
/// An ASTVisitor for building SIL function calls. /// An ASTVisitor for building SIL function calls.
@@ -915,7 +918,8 @@ public:
for (auto callSite : CallSites) { for (auto callSite : CallSites) {
addSite(callSite, false); 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)) { if (auto selfApply = dyn_cast_or_null<ApplyExpr>(SelfApplyExpr)) {
addSite(selfApply, otherCtorRefUsesAllocating); addSite(selfApply, otherCtorRefUsesAllocating);
} }
@@ -1022,6 +1026,68 @@ public:
bool requiresAllocRefDynamic = false; bool requiresAllocRefDynamic = false;
// Determine whether the method is dynamically dispatched. // 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) { if (e->getAccessSemantics() != AccessSemantics::Ordinary) {
isDynamicallyDispatched = false; isDynamicallyDispatched = false;
} else { } else {
@@ -1222,78 +1288,6 @@ public:
return subs.slice(innerIdx); 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) { void visitFunctionConversionExpr(FunctionConversionExpr *e) {
// FIXME: Check whether this function conversion requires us to build a // FIXME: Check whether this function conversion requires us to build a
// thunk. // thunk.
@@ -3451,9 +3445,6 @@ static Callee getBaseAccessorFunctionRef(SILGenFunction &gen,
assert(!isDirectUse && "direct use of protocol accessor?"); assert(!isDirectUse && "direct use of protocol accessor?");
assert(!isSuper && "super call to protocol method?"); assert(!isSuper && "super call to protocol method?");
// The protocol self is implicitly decurried.
substAccessorType = cast<AnyFunctionType>(substAccessorType.getResult());
return prepareArchetypeCallee(gen, loc, constant, selfValue, return prepareArchetypeCallee(gen, loc, constant, selfValue,
substAccessorType, substitutions); substAccessorType, substitutions);
} }

View File

@@ -1803,13 +1803,6 @@ RValue RValueEmitter::visitMemberRefExpr(MemberRefExpr *E, SGFContext C) {
SGF.B.createMetatype(E, SGF.getLoweredLoadableType(E->getType())); SGF.B.createMetatype(E, SGF.getLoweredLoadableType(E->getType()));
return RValue(SGF, E, ManagedValue::forUnmanaged(MT)); 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 // 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 // emit the base rvalue's member using special logic that will let us avoid

View File

@@ -472,7 +472,7 @@ namespace {
locator, locator, implicit, semantics, locator, locator, implicit, semantics,
/*isDynamic=*/false); /*isDynamic=*/false);
if (!result) if (!result)
return nullptr;; return nullptr;
return result; return result;
} }
@@ -967,11 +967,11 @@ namespace {
return ref; return ref;
} }
// For types, properties, and members of archetypes, build // For types and properties, build member references.
// member references. if (isa<TypeDecl>(member) || isa<VarDecl>(member)) {
if (isDependentConformingRef || isa<TypeDecl>(member) ||
isa<VarDecl>(member)) {
assert(!dynamicSelfFnType && "Converted type doesn't make sense here"); assert(!dynamicSelfFnType && "Converted type doesn't make sense here");
assert(baseIsInstance || !member->isInstanceMember());
auto memberRefExpr auto memberRefExpr
= new (context) MemberRefExpr(base, dotLoc, memberRef, = new (context) MemberRefExpr(base, dotLoc, memberRef,
memberLoc, Implicit, semantics); memberLoc, Implicit, semantics);
@@ -2226,7 +2226,9 @@ namespace {
newRef = convertUnavailableToOptional(newRef,decl, expr->getLoc(), newRef = convertUnavailableToOptional(newRef,decl, expr->getLoc(),
choice.getReasonUnavailable(cs)); choice.getReasonUnavailable(cs));
} }
recordUnsupportedPartialApply(expr, newRef);
return newRef; return newRef;
} }
@@ -2360,7 +2362,10 @@ namespace {
locator, expr->isSpecialized(), locator, expr->isSpecialized(),
expr->isImplicit(), expr->isImplicit(),
AccessSemantics::Ordinary); AccessSemantics::Ordinary);
if (auto newDeclRef = dyn_cast<DeclRefExpr>(newRef))
recordUnsupportedPartialApply(newDeclRef, newDeclRef);
if (choice.isPotentiallyUnavailable()) { if (choice.isPotentiallyUnavailable()) {
newRef = convertUnavailableToOptional(newRef, decl, expr->getLoc(), newRef = convertUnavailableToOptional(newRef, decl, expr->getLoc(),
choice.getReasonUnavailable(cs)); choice.getReasonUnavailable(cs));
@@ -2488,23 +2493,22 @@ namespace {
} }
private: private:
struct MemberPartialApplication { // Selector for the partial_application_of_function_invalid diagnostic
// message.
struct PartialApplication {
unsigned level : 29; unsigned level : 29;
// Selector for the partial_application_of_method_invalid diagnostic
// message.
enum : unsigned { enum : unsigned {
Struct, Function,
Enum, MutatingMethod,
Archetype, ObjCProtocolMethod
Protocol
}; };
unsigned kind : 3; unsigned kind : 3;
}; };
// A map used to track partial applications of methods to require that they // Partial applications of functions with inout parameters is not permitted,
// be fully applied. Partial applications of value types would capture // so we track them to ensure they are fully applied. The map value is the
// 'self' as an inout and hide any mutation of 'self', which is surprising. // number of remaining applications.
llvm::SmallDenseMap<Expr*, MemberPartialApplication, 2> llvm::SmallDenseMap<Expr*, PartialApplication, 2>
InvalidPartialApplications; InvalidPartialApplications;
/// A list of "suspicious" optional injections that come from /// A list of "suspicious" optional injections that come from
@@ -2575,49 +2579,10 @@ namespace {
AccessSemantics::Ordinary, AccessSemantics::Ordinary,
isDynamic, isDynamic,
reason); reason);
// If this is an application of a mutating method, if (auto applyExpr = dyn_cast<ApplyExpr>(member))
// arrange for us to check that it gets fully applied. advancePartialApplyExpr(applyExpr, applyExpr);
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 (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; return member;
} }
@@ -2884,29 +2849,107 @@ namespace {
llvm_unreachable("Already type-checked"); 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) { Expr *visitApplyExpr(ApplyExpr *expr) {
auto result = finishApply(expr, expr->getType(), auto result = finishApply(expr, expr->getType(),
ConstraintLocatorBuilder( ConstraintLocatorBuilder(
cs.getConstraintLocator(expr))); cs.getConstraintLocator(expr)));
// See if this application advanced a partial value type application. // Record a partial value type application, if necessary.
Expr *fn = expr->getFn()->getSemanticsProvidingExpr(); advancePartialApplyExpr(expr, result);
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
}
});
}
return result; return result;
} }
@@ -3412,7 +3455,7 @@ namespace {
for (auto &unapplied : InvalidPartialApplications) { for (auto &unapplied : InvalidPartialApplications) {
unsigned kind = unapplied.second.kind; unsigned kind = unapplied.second.kind;
tc.diagnose(unapplied.first->getLoc(), tc.diagnose(unapplied.first->getLoc(),
diag::partial_application_of_method_invalid, diag::partial_application_of_function_invalid,
kind); kind);
} }

View File

@@ -1010,14 +1010,7 @@ void ConstraintSystem::openGeneric(
/// declared. /// declared.
static void addSelfConstraint(ConstraintSystem &cs, Type objectTy, Type selfTy, static void addSelfConstraint(ConstraintSystem &cs, Type objectTy, Type selfTy,
ConstraintLocatorBuilder locator){ ConstraintLocatorBuilder locator){
// When referencing a protocol member, we need the object type to be usable assert(!selfTy->is<ProtocolType>());
// 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;
}
// Otherwise, use a subtype constraint for classes to cope with inheritance. // Otherwise, use a subtype constraint for classes to cope with inheritance.
if (selfTy->getClassOrBoundGenericClass()) { if (selfTy->getClassOrBoundGenericClass()) {

View File

@@ -126,23 +126,16 @@ protocol P {
mutating func mut(x: Int) mutating func mut(x: Int)
} }
@objc
protocol P2 {
func bar(x: Int)
}
func generic<T: P>(var t: T) { func generic<T: P>(var t: T) {
_ = t.bar _ = 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) var _ : () = t.bar(0)
} }
func existential(var p: P, p2 : P2) { func existential(var p: P) {
p.mut(1) p.mut(1)
_ = p.bar _ = p.bar
_ = p.mut // expected-error{{partial application of 'mutating' protocol method is not allowed}} _ = p.mut // expected-error{{partial application of 'mutating' method is not allowed}}
_ = p2.bar
var _ : () = p.bar(0) 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) let p : P = S(offset: 4)
print("\(p.scale(5)(y: 7))\n", appendNewline: false) print("\(p.scale(5)(y: 7))\n", appendNewline: false)
// CHECK: 39 // 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 // RUN: %target-swift-frontend -emit-silgen %s | FileCheck %s
protocol Foo { protocol Foo {
static func foo() static func staticFunc()
func instanceFunc()
} }
func getFoo<T: Foo>(t: T.Type) -> () -> () { // CHECK-LABEL: sil hidden @_TF21partial_apply_generic14getStaticFunc1uRq_S_3Foo_FMq_FT_T_
return t.foo 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-LABEL: sil shared @_TZFP21partial_apply_generic3Foo10staticFuncuRq_S0__FMq_FT_T_
// CHECK: function_ref @_TZFP21partial_apply_generic3Foo3foouRq_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 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}}