diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 6c9a75fa68c27..3fa6c530a3890 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1349,20 +1349,53 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2, return getTypeMatchSuccess(); } +// Determine whether conversion is allowed between two function types +// based on their representations. +static bool +isConversionAllowedBetween(FunctionTypeRepresentation rep1, + FunctionTypeRepresentation rep2) { + auto isThin = [](FunctionTypeRepresentation rep) { + return rep == FunctionTypeRepresentation::CFunctionPointer || + rep == FunctionTypeRepresentation::Thin; + }; + + // Allowing "thin" (c, thin) to "thin" conventions + if (isThin(rep1) && isThin(rep2)) + return true; + + // Allowing all to "thick" (swift, block) conventions + // "thin" (c, thin) to "thick" or "thick" to "thick" + if (rep2 == FunctionTypeRepresentation::Swift || + rep2 == FunctionTypeRepresentation::Block) + return true; + + return rep1 == rep2; +} + // Returns 'false' (i.e. no error) if it is legal to match functions with the // corresponding function type representations and the given match kind. static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1, FunctionTypeRepresentation rep2, - ConstraintKind kind) { + ConstraintKind kind, + ConstraintLocatorBuilder locator) { switch (kind) { case ConstraintKind::Bind: case ConstraintKind::BindParam: case ConstraintKind::BindToPointerType: case ConstraintKind::Equal: return rep1 != rep2; + + case ConstraintKind::Subtype: { + auto last = locator.last(); + if (!(last && last->is())) + return false; + + // Inverting the result because matchFunctionRepresentations + // returns false in conversions are allowed. + return !isConversionAllowedBetween(rep1, rep2); + } case ConstraintKind::OpaqueUnderlyingType: - case ConstraintKind::Subtype: case ConstraintKind::Conversion: case ConstraintKind::BridgingConversion: case ConstraintKind::ArgumentConversion: @@ -1657,7 +1690,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (matchFunctionRepresentations(func1->getExtInfo().getRepresentation(), func2->getExtInfo().getRepresentation(), - kind)) { + kind, locator)) { return getTypeMatchFailure(locator); } diff --git a/validation-test/compiler_crashers_2_fixed/sr12723.swift b/validation-test/compiler_crashers_2_fixed/sr12723.swift new file mode 100644 index 0000000000000..b04103a9f7a8e --- /dev/null +++ b/validation-test/compiler_crashers_2_fixed/sr12723.swift @@ -0,0 +1,81 @@ +// RUN: %target-swift-emit-silgen %s -verify + +func SR12723_thin(_: (@convention(thin) () -> Void) -> Void) {} +func SR12723_block(_: (@convention(block) () -> Void) -> Void) {} +func SR12723_c(_: (@convention(c) () -> Void) -> Void) {} + +func SR12723_function(_: () -> Void) {} + +func context() { + SR12723_c(SR12723_function) + + SR12723_block(SR12723_function) + + SR12723_thin(SR12723_function) +} + +struct SR12723_C { + let function: (@convention(c) () -> Void) -> Void +} + +struct SR12723_Thin { + let function: (@convention(thin) () -> Void) -> Void +} + +struct SR12723_Block { + let function: (@convention(block) () -> Void) -> Void +} + +func proxy(_ f: (() -> Void) -> Void) { + let a = 1 + f { print(a) } +} + +func cContext() { + let c = SR12723_C { app in app() } + + proxy(c.function) + // expected-error@-1 {{cannot convert value of type '(@convention(c) () -> Void) -> Void' to expected argument type '(() -> Void) -> Void'}} + + let _ : (@convention(block) () -> Void) -> Void = c.function + // expected-error@-1 {{cannot convert value of type '(@convention(c) () -> Void) -> Void' to specified type '(@convention(block) () -> Void) -> Void'}} + + let _ : (@convention(c) () -> Void) -> Void = c.function // OK + + let _ : (@convention(thin) () -> Void) -> Void = c.function // OK + + let _ : (() -> Void) -> Void = c.function + // expected-error@-1 {{cannot convert value of type '(@convention(c) () -> Void) -> Void' to specified type '(() -> Void) -> Void'}} + +} + +func thinContext() { + let thin = SR12723_Thin { app in app() } + + proxy(thin.function) + // expected-error@-1 {{cannot convert value of type '(@convention(thin) () -> Void) -> Void' to expected argument type '(() -> Void) -> Void'}} + + let _ : (@convention(block) () -> Void) -> Void = thin.function + // expected-error@-1 {{cannot convert value of type '(@convention(thin) () -> Void) -> Void' to specified type '(@convention(block) () -> Void) -> Void'}} + + let _ : (@convention(c) () -> Void) -> Void = thin.function // OK + + let _ : (@convention(thin) () -> Void) -> Void = thin.function // OK + + let _ : (() -> Void) -> Void = thin.function + // expected-error@-1 {{cannot convert value of type '(@convention(thin) () -> Void) -> Void' to specified type '(() -> Void) -> Void'}} +} + +func blockContext() { + let block = SR12723_Block { app in app() } + + proxy(block.function) + + let _ : (@convention(block) () -> Void) -> Void = block.function // OK + + let _ : (@convention(c) () -> Void) -> Void = block.function // OK + + let _ : (@convention(thin) () -> Void) -> Void = block.function // OK + + let _ : (() -> Void) -> Void = block.function // OK +}