gpui2: Type-erase futures. (#3266)

Antonio Scandurra created

Project2's LLVM IR size is ~33-44% bigger than project1 due to the fact
that in gpui2 we call async_task::spawn(_local) with impl Future instead
of dyn Future, which leads to quite a few more instantiations of
RawTask.

LLVM-IR size for project2:
|  build_type  |  main   |  this branch  | project1 |
|  debug       | 2617795 |    2022814    | 1817866  |
|  release     | 4439033 |    3715086    | 3314489  |

Note that this PR is in line with what was done in GPUI1 (we've also
boxed futures there).
Release Notes:

- N/A

Change summary

crates/gpui2/src/executor.rs | 33 +++++++++++++++++++++++----------
1 file changed, 23 insertions(+), 10 deletions(-)

Detailed changes

crates/gpui2/src/executor.rs 🔗

@@ -68,7 +68,8 @@ impl<T> Future for Task<T> {
         }
     }
 }
-
+type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
+type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
 impl BackgroundExecutor {
     pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
         Self { dispatcher }
@@ -81,10 +82,16 @@ impl BackgroundExecutor {
         R: Send + 'static,
     {
         let dispatcher = self.dispatcher.clone();
-        let (runnable, task) =
-            async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
-        runnable.schedule();
-        Task::Spawned(task)
+        fn inner<R: Send + 'static>(
+            dispatcher: Arc<dyn PlatformDispatcher>,
+            future: AnyFuture<R>,
+        ) -> Task<R> {
+            let (runnable, task) =
+                async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
+            runnable.schedule();
+            Task::Spawned(task)
+        }
+        inner::<R>(dispatcher, Box::pin(future))
     }
 
     #[cfg(any(test, feature = "test-support"))]
@@ -243,11 +250,17 @@ impl ForegroundExecutor {
         R: 'static,
     {
         let dispatcher = self.dispatcher.clone();
-        let (runnable, task) = async_task::spawn_local(future, move |runnable| {
-            dispatcher.dispatch_on_main_thread(runnable)
-        });
-        runnable.schedule();
-        Task::Spawned(task)
+        fn inner<R: 'static>(
+            dispatcher: Arc<dyn PlatformDispatcher>,
+            future: AnyLocalFuture<R>,
+        ) -> Task<R> {
+            let (runnable, task) = async_task::spawn_local(future, move |runnable| {
+                dispatcher.dispatch_on_main_thread(runnable)
+            });
+            runnable.schedule();
+            Task::Spawned(task)
+        }
+        inner::<R>(dispatcher, Box::pin(future))
     }
 }