Skip to content

Commit c889ca5

Browse files
committed
Sema/SILGen: Move invalid @convention(c) conversion check to SILGen
This removes a bit of global state from the TypeChecker class, and allows C function pointers to be formed to closures that capture local functions, but have no transitive captures.
1 parent e1722dc commit c889ca5

File tree

8 files changed

+61
-73
lines changed

8 files changed

+61
-73
lines changed

Diff for: include/swift/AST/DiagnosticsSIL.def

+6
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ ERROR(unsupported_c_function_pointer_conversion,none,
113113
"C function pointer signature %0 is not compatible with expected type %1",
114114
(Type, Type))
115115

116+
ERROR(c_function_pointer_from_function_with_context,none,
117+
"a C function pointer cannot be formed from a "
118+
"%select{local function|closure}0 that captures "
119+
"%select{context|generic parameters|dynamic Self type|<<error>}1",
120+
(bool, unsigned))
121+
116122
ERROR(objc_selector_malformed,none,"the type ObjectiveC.Selector is malformed",
117123
())
118124

Diff for: include/swift/AST/DiagnosticsSema.def

-5
Original file line numberDiff line numberDiff line change
@@ -1121,11 +1121,6 @@ ERROR(c_function_pointer_from_method,none,
11211121
ERROR(c_function_pointer_from_generic_function,none,
11221122
"a C function pointer cannot be formed from a reference to a generic "
11231123
"function", ())
1124-
ERROR(c_function_pointer_from_function_with_context,none,
1125-
"a C function pointer cannot be formed from a "
1126-
"%select{local function|closure}0 that captures "
1127-
"%select{context|generic parameters|dynamic Self type|<<error>}1",
1128-
(bool, unsigned))
11291124
ERROR(invalid_autoclosure_forwarding,none,
11301125
"add () to forward @autoclosure parameter", ())
11311126

Diff for: lib/SILGen/SILGenExpr.cpp

+26-1
Original file line numberDiff line numberDiff line change
@@ -1537,9 +1537,34 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF,
15371537
}
15381538

15391539
// Produce a reference to the C-compatible entry point for the function.
1540-
SILDeclRef constant(loc, /*uncurryLevel*/ 0, /*foreign*/ true);
1540+
SILDeclRef constant(loc, /*curried*/ false, /*foreign*/ true);
15411541
SILConstantInfo constantInfo = SGF.getConstantInfo(constant);
15421542

1543+
// C function pointers cannot capture anything from their context.
1544+
auto captures = SGF.SGM.Types.getLoweredLocalCaptures(
1545+
*constant.getAnyFunctionRef());
1546+
1547+
if (captures.hasGenericParamCaptures() ||
1548+
captures.hasDynamicSelfCapture() ||
1549+
captures.hasLocalCaptures() ||
1550+
captures.hasOpaqueValueCapture()) {
1551+
unsigned kind;
1552+
if (captures.hasLocalCaptures())
1553+
kind = 0;
1554+
else if (captures.hasGenericParamCaptures())
1555+
kind = 1;
1556+
else if (captures.hasLocalCaptures())
1557+
kind = 2;
1558+
else
1559+
kind = 3;
1560+
SGF.SGM.diagnose(expr->getLoc(),
1561+
diag::c_function_pointer_from_function_with_context,
1562+
/*closure*/ constant.hasClosureExpr(),
1563+
kind);
1564+
1565+
return SGF.emitUndef(constantInfo.getSILType());
1566+
}
1567+
15431568
return convertCFunctionSignature(
15441569
SGF, conversionExpr,
15451570
constantInfo.getSILType(),

Diff for: lib/Sema/CSApply.cpp

+1-5
Original file line numberDiff line numberDiff line change
@@ -5795,8 +5795,6 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr,
57955795
} else if (fn->getGenericParams()) {
57965796
tc.diagnose(expr->getLoc(),
57975797
diag::c_function_pointer_from_generic_function);
5798-
} else {
5799-
tc.maybeDiagnoseCaptures(expr, fn);
58005798
}
58015799
};
58025800

@@ -5817,10 +5815,8 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr,
58175815
semanticExpr = capture->getClosureBody();
58185816

58195817
// Can convert a literal closure that doesn't capture context.
5820-
if (auto closure = dyn_cast<ClosureExpr>(semanticExpr)) {
5821-
tc.maybeDiagnoseCaptures(expr, closure);
5818+
if (auto closure = dyn_cast<ClosureExpr>(semanticExpr))
58225819
return;
5823-
}
58245820

58255821
tc.diagnose(expr->getLoc(),
58265822
diag::invalid_c_function_pointer_conversion_expr);

Diff for: lib/Sema/TypeCheckCaptures.cpp

-36
Original file line numberDiff line numberDiff line change
@@ -590,35 +590,6 @@ class FindCapturedVars : public ASTWalker {
590590

591591
} // end anonymous namespace
592592

593-
void TypeChecker::maybeDiagnoseCaptures(Expr *E, AnyFunctionRef AFR) {
594-
if (!AFR.getCaptureInfo().hasBeenComputed()) {
595-
// The capture list is not always initialized by the point we reference
596-
// it. Remember we formed a C function pointer so we can diagnose later
597-
// if necessary.
598-
LocalCFunctionPointers[AFR].push_back(E);
599-
return;
600-
}
601-
602-
if (AFR.getCaptureInfo().hasGenericParamCaptures() ||
603-
AFR.getCaptureInfo().hasDynamicSelfCapture() ||
604-
AFR.getCaptureInfo().hasLocalCaptures() ||
605-
AFR.getCaptureInfo().hasOpaqueValueCapture()) {
606-
unsigned kind;
607-
if (AFR.getCaptureInfo().hasLocalCaptures())
608-
kind = 0;
609-
else if (AFR.getCaptureInfo().hasGenericParamCaptures())
610-
kind = 1;
611-
else if (AFR.getCaptureInfo().hasLocalCaptures())
612-
kind = 2;
613-
else
614-
kind = 3;
615-
diagnose(E->getLoc(),
616-
diag::c_function_pointer_from_function_with_context,
617-
/*closure*/ AFR.getAbstractClosureExpr() != nullptr,
618-
kind);
619-
}
620-
}
621-
622593
void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
623594
if (AFR.getCaptureInfo().hasBeenComputed())
624595
return;
@@ -671,13 +642,6 @@ void TypeChecker::computeCaptures(AnyFunctionRef AFR) {
671642
}
672643
}
673644
}
674-
675-
// Diagnose if we have local captures and there were C pointers formed to
676-
// this function before we computed captures.
677-
auto cFunctionPointers = LocalCFunctionPointers.find(AFR);
678-
if (cFunctionPointers != LocalCFunctionPointers.end())
679-
for (auto *expr : cFunctionPointers->second)
680-
maybeDiagnoseCaptures(expr, AFR);
681645
}
682646

683647
void TypeChecker::checkPatternBindingCaptures(NominalTypeDecl *typeDecl) {

Diff for: lib/Sema/TypeChecker.h

-11
Original file line numberDiff line numberDiff line change
@@ -569,13 +569,6 @@ class TypeChecker final : public LazyResolver {
569569
/// will need to compute captures for.
570570
std::vector<AbstractClosureExpr *> ClosuresWithUncomputedCaptures;
571571

572-
/// A set of local functions from which C function pointers are derived.
573-
///
574-
/// This is used to diagnose the use of local functions with captured context
575-
/// as C function pointers when the function's captures have not yet been
576-
/// computed.
577-
llvm::DenseMap<AnyFunctionRef, std::vector<Expr*>> LocalCFunctionPointers;
578-
579572
private:
580573
/// The # of times we have performed typo correction.
581574
unsigned NumTypoCorrections = 0;
@@ -1434,10 +1427,6 @@ class TypeChecker final : public LazyResolver {
14341427
/// Type-check a for-each loop's pattern binding and sequence together.
14351428
bool typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt);
14361429

1437-
/// Lazily diagnose conversions to C function pointers of closures
1438-
/// with captures.
1439-
void maybeDiagnoseCaptures(Expr *E, AnyFunctionRef AFR);
1440-
14411430
/// Compute the set of captures for the given function or closure.
14421431
void computeCaptures(AnyFunctionRef AFR);
14431432

Diff for: test/Parse/c_function_pointers.swift

-15
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,13 @@ class C {
1111
}
1212

1313
if true {
14-
var x = 0
1514
func local() -> Int { return 0 }
16-
func localWithContext() -> Int { return x }
1715

1816
let a: @convention(c) () -> Int = global
1917
let _: @convention(c) () -> Int = main.global
2018
let _: @convention(c) () -> Int = { 0 }
2119
let _: @convention(c) () -> Int = local
2220

23-
// Can't convert a closure with context to a C function pointer
24-
let _: @convention(c) () -> Int = { x } // expected-error{{cannot be formed from a closure that captures context}}
25-
let _: @convention(c) () -> Int = { [x] in x } // expected-error{{cannot be formed from a closure that captures context}}
26-
let _: @convention(c) () -> Int = localWithContext // expected-error{{cannot be formed from a local function that captures context}}
27-
2821
// Can't convert a closure value to a C function pointer
2922
let global2 = global
3023
let _: @convention(c) () -> Int = global2 // expected-error{{can only be formed from a reference to a 'func' or a literal closure}}
@@ -51,14 +44,6 @@ if true {
5144
handler(iuo_global) // expected-error{{a C function pointer can only be formed from a reference to a 'func' or a literal closure}}
5245
}
5346

54-
class Generic<X : C> {
55-
func f<Y : C>(_ y: Y) {
56-
let _: @convention(c) () -> Int = { return 0 }
57-
let _: @convention(c) () -> Int = { return X.staticMethod() } // expected-error{{cannot be formed from a closure that captures generic parameters}}
58-
let _: @convention(c) () -> Int = { return Y.staticMethod() } // expected-error{{cannot be formed from a closure that captures generic parameters}}
59-
}
60-
}
61-
6247
func genericFunc<T>(_ t: T) -> T { return t }
6348

6449
let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be formed from a reference to a generic function}}

Diff for: test/SILGen/c_function_pointers.swift

+28
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
// RUN: %target-swift-emit-silgen -verify %s | %FileCheck %s
22

3+
if true {
4+
var x = 0
5+
func local() -> Int { return 0 }
6+
func localWithContext() -> Int { return x }
7+
func transitiveWithoutContext() -> Int { return local() }
8+
9+
// Can't convert a closure with context to a C function pointer
10+
let _: @convention(c) () -> Int = { x } // expected-error{{cannot be formed from a closure that captures context}}
11+
let _: @convention(c) () -> Int = { [x] in x } // expected-error{{cannot be formed from a closure that captures context}}
12+
let _: @convention(c) () -> Int = localWithContext // expected-error{{cannot be formed from a local function that captures context}}
13+
14+
let _: @convention(c) () -> Int = local
15+
let _: @convention(c) () -> Int = transitiveWithoutContext
16+
}
17+
18+
class C {
19+
static func staticMethod() -> Int { return 0 }
20+
class func classMethod() -> Int { return 0 }
21+
}
22+
23+
class Generic<X : C> {
24+
func f<Y : C>(_ y: Y) {
25+
let _: @convention(c) () -> Int = { return 0 }
26+
let _: @convention(c) () -> Int = { return X.staticMethod() } // expected-error{{cannot be formed from a closure that captures generic parameters}}
27+
let _: @convention(c) () -> Int = { return Y.staticMethod() } // expected-error{{cannot be formed from a closure that captures generic parameters}}
28+
}
29+
}
30+
331
func values(_ arg: @escaping @convention(c) (Int) -> Int) -> @convention(c) (Int) -> Int {
432
return arg
533
}

0 commit comments

Comments
 (0)