diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 6557e3649377..90674d151173 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1685,19 +1685,30 @@ object Types { case _ => resultType } - /** Determine the expected function type from the prototype. If multiple - * function types are found in a union or intersection, their intersection - * is returned. If no function type is found, Any is returned. + /** Determine the expected function type from the prototype. + * If no function type is found, NoType is returned. If multiple + * function types are found in an intersection, their intersection + * is returned. This works since `&` invokes `TypeComparer.distributeAnd`, which + * ensures that `(A1 => B1) & (A2 => B2)` simplifies to `(A1 | A2) => (B1 & B2)`, + * so the result is again a function type. An analogous distribution mechanism + * does not exist for `|`. Therefore, a union of function types also yields `NoType`, + * since we cannot determine a single expected function type. */ def findFunctionType(using Context): Type = dealias match - case tp: AndOrType => + case tp: AndType => tp.tp1.findFunctionType & tp.tp2.findFunctionType + case tp: OrType => + val tf1 = tp.tp1.findFunctionType + val tf2 = tp.tp2.findFunctionType + if !tf1.exists then tf2 + else if !tf2.exists then tf1 + else NoType case t if defn.isNonRefinedFunction(t) => t case t @ SAMType(_) => t case _ => - defn.AnyType + NoType /** This type seen as a TypeBounds */ final def bounds(using Context): TypeBounds = this match { diff --git a/tests/neg/i11694.scala b/tests/neg/i11694.scala index 67138fd5a7eb..d43fe4aa99e3 100644 --- a/tests/neg/i11694.scala +++ b/tests/neg/i11694.scala @@ -5,8 +5,8 @@ def test1 = { def f21: (Int => Int) | Null = x => x + 1 def f22: Null | (Int => Int) = x => x + 1 - def f31: (Int => Int) | (Int => Int) = x => x + 1 - def f32: (Int => Int) | (Int => Int) | Unit = x => x + 1 + def f31: (Int => Int) | (Int => Int) = x => x + 1 // error + def f32: (Int => Int) | (Int => Int) | Unit = x => x + 1 // error def f41: (Int => Int) & (Int => Int) = x => x + 1 def f42: (Int => Int) & (Int => Int) & Any = x => x + 1 diff --git a/tests/pos/i15460.scala b/tests/pos/i15460.scala new file mode 100644 index 000000000000..c247cb0ae878 --- /dev/null +++ b/tests/pos/i15460.scala @@ -0,0 +1,5 @@ +type C = (() => Int) | (() => String) + +def foo(c: C): Unit = () + +val _ = foo(() => 1)