Remove tags
diff --git a/examples/block.rs b/examples/block.rs
deleted file mode 100644
index dea10be..0000000
--- a/examples/block.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-//! A simple implementation of `block_on`.
-
-use std::cell::RefCell;
-use std::future::Future;
-use std::task::{Context, Poll, Waker};
-use std::thread;
-use std::time::Duration;
-
-use crossbeam::sync::Parker;
-use futures::channel::oneshot;
-
-/// Runs a future to completion on the current thread.
-fn block_on<F: Future>(future: F) -> F::Output {
-    // Pin the future on the stack.
-    futures::pin_mut!(future);
-
-    thread_local! {
-        // Parker and waker associated with the current thread.
-        static CACHE: RefCell<(Parker, Waker)> = {
-            let parker = Parker::new();
-            let unparker = parker.unparker().clone();
-            let waker = async_task::waker_fn(move || unparker.unpark());
-            RefCell::new((parker, waker))
-        };
-    }
-
-    CACHE.with(|cache| {
-        // Panic if `block_on()` is called recursively.
-        let (parker, waker) = &mut *cache.try_borrow_mut().ok().expect("recursive block_on()");
-
-        // Create the task context.
-        let cx = &mut Context::from_waker(&waker);
-
-        // Keep polling the future until completion.
-        loop {
-            match future.as_mut().poll(cx) {
-                Poll::Ready(output) => return output,
-                Poll::Pending => parker.park(),
-            }
-        }
-    })
-}
-
-fn main() {
-    let (s, r) = oneshot::channel();
-
-    // Spawn a thread that will send a message through the channel.
-    thread::spawn(move || {
-        thread::sleep(Duration::from_secs(1));
-        s.send("Hello, world!").unwrap();
-    });
-
-    // Block until the message is received.
-    let msg = block_on(async {
-        println!("Awaiting...");
-        r.await.unwrap()
-    });
-
-    println!("{}", msg);
-}
diff --git a/examples/panic-propagation.rs b/examples/panic-propagation.rs
index 05ec85a..8d9be39 100644
--- a/examples/panic-propagation.rs
+++ b/examples/panic-propagation.rs
@@ -6,13 +6,12 @@
 use std::task::{Context, Poll};
 use std::thread;
 
+use async_task::Task;
 use crossbeam::channel::{unbounded, Sender};
 use futures::executor;
 use futures::future::FutureExt;
 use lazy_static::lazy_static;
 
-type Task = async_task::Task<()>;
-
 /// Spawns a future on the executor.
 fn spawn<F, R>(future: F) -> JoinHandle<R>
 where
@@ -41,7 +40,7 @@
 
     // Create a task that is scheduled by sending itself into the channel.
     let schedule = |t| QUEUE.send(t).unwrap();
-    let (task, handle) = async_task::spawn(future, schedule, ());
+    let (task, handle) = async_task::spawn(future, schedule);
 
     // Schedule the task by sending it into the channel.
     task.schedule();
@@ -51,7 +50,7 @@
 }
 
 /// A join handle that propagates panics inside the task.
-struct JoinHandle<R>(async_task::JoinHandle<thread::Result<R>, ()>);
+struct JoinHandle<R>(async_task::JoinHandle<thread::Result<R>>);
 
 impl<R> Future for JoinHandle<R> {
     type Output = Option<R>;
diff --git a/examples/panic-result.rs b/examples/panic-result.rs
index b93f060..10da772 100644
--- a/examples/panic-result.rs
+++ b/examples/panic-result.rs
@@ -4,14 +4,12 @@
 use std::panic::AssertUnwindSafe;
 use std::thread;
 
+use async_task::{JoinHandle, Task};
 use crossbeam::channel::{unbounded, Sender};
 use futures::executor;
 use futures::future::FutureExt;
 use lazy_static::lazy_static;
 
-type Task = async_task::Task<()>;
-type JoinHandle<T> = async_task::JoinHandle<T, ()>;
-
 /// Spawns a future on the executor.
 fn spawn<F, R>(future: F) -> JoinHandle<thread::Result<R>>
 where
@@ -40,7 +38,7 @@
 
     // Create a task that is scheduled by sending itself into the channel.
     let schedule = |t| QUEUE.send(t).unwrap();
-    let (task, handle) = async_task::spawn(future, schedule, ());
+    let (task, handle) = async_task::spawn(future, schedule);
 
     // Schedule the task by sending it into the channel.
     task.schedule();
diff --git a/examples/spawn-local.rs b/examples/spawn-local.rs
index 4e66c32..4349a5b 100644
--- a/examples/spawn-local.rs
+++ b/examples/spawn-local.rs
@@ -4,11 +4,9 @@
 use std::future::Future;
 use std::rc::Rc;
 
+use async_task::{JoinHandle, Task};
 use crossbeam::channel::{unbounded, Receiver, Sender};
 
-type Task = async_task::Task<()>;
-type JoinHandle<T> = async_task::JoinHandle<T, ()>;
-
 thread_local! {
     // A channel that holds scheduled tasks.
     static QUEUE: (Sender<Task>, Receiver<Task>) = unbounded();
@@ -22,7 +20,7 @@
 {
     // Create a task that is scheduled by sending itself into the channel.
     let schedule = |t| QUEUE.with(|(s, _)| s.send(t).unwrap());
-    let (task, handle) = async_task::spawn_local(future, schedule, ());
+    let (task, handle) = async_task::spawn_local(future, schedule);
 
     // Schedule the task by sending it into the queue.
     task.schedule();
diff --git a/examples/spawn-on-thread.rs b/examples/spawn-on-thread.rs
index 95214ed..5c6bf9b 100644
--- a/examples/spawn-on-thread.rs
+++ b/examples/spawn-on-thread.rs
@@ -4,11 +4,10 @@
 use std::sync::Arc;
 use std::thread;
 
+use async_task::JoinHandle;
 use crossbeam::channel;
 use futures::executor;
 
-type JoinHandle<T> = async_task::JoinHandle<T, ()>;
-
 /// Spawns a future on a new dedicated thread.
 ///
 /// The returned handle can be used to await the output of the future.
@@ -31,7 +30,7 @@
 
     // Create a task that is scheduled by sending itself into the channel.
     let schedule = move |t| s.upgrade().unwrap().send(t).unwrap();
-    let (task, handle) = async_task::spawn(future, schedule, ());
+    let (task, handle) = async_task::spawn(future, schedule);
 
     // Schedule the task by sending it into the channel.
     task.schedule();
diff --git a/examples/spawn.rs b/examples/spawn.rs
index 9db7215..54df1c2 100644
--- a/examples/spawn.rs
+++ b/examples/spawn.rs
@@ -4,13 +4,11 @@
 use std::panic::catch_unwind;
 use std::thread;
 
+use async_task::{JoinHandle, Task};
 use crossbeam::channel::{unbounded, Sender};
 use futures::executor;
 use lazy_static::lazy_static;
 
-type Task = async_task::Task<()>;
-type JoinHandle<T> = async_task::JoinHandle<T, ()>;
-
 /// Spawns a future on the executor.
 fn spawn<F, R>(future: F) -> JoinHandle<R>
 where
@@ -36,7 +34,7 @@
 
     // Create a task that is scheduled by sending itself into the channel.
     let schedule = |t| QUEUE.send(t).unwrap();
-    let (task, handle) = async_task::spawn(future, schedule, ());
+    let (task, handle) = async_task::spawn(future, schedule);
 
     // Schedule the task by sending it into the channel.
     task.schedule();
diff --git a/examples/task-id.rs b/examples/task-id.rs
deleted file mode 100644
index 2a7bcf7..0000000
--- a/examples/task-id.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-//! An executor that assigns an ID to every spawned task.
-
-use std::cell::Cell;
-use std::future::Future;
-use std::panic::catch_unwind;
-use std::thread;
-
-use crossbeam::atomic::AtomicCell;
-use crossbeam::channel::{unbounded, Sender};
-use futures::executor;
-use lazy_static::lazy_static;
-
-#[derive(Clone, Copy, Debug)]
-struct TaskId(usize);
-
-type Task = async_task::Task<TaskId>;
-type JoinHandle<T> = async_task::JoinHandle<T, TaskId>;
-
-thread_local! {
-    /// The ID of the current task.
-    static TASK_ID: Cell<Option<TaskId>> = Cell::new(None);
-}
-
-/// Returns the ID of the currently executing task.
-///
-/// Returns `None` if called outside the runtime.
-fn task_id() -> Option<TaskId> {
-    TASK_ID.with(|id| id.get())
-}
-
-/// Spawns a future on the executor.
-fn spawn<F, R>(future: F) -> JoinHandle<R>
-where
-    F: Future<Output = R> + Send + 'static,
-    R: Send + 'static,
-{
-    lazy_static! {
-        // A channel that holds scheduled tasks.
-        static ref QUEUE: Sender<Task> = {
-            let (sender, receiver) = unbounded::<Task>();
-
-            // Start the executor thread.
-            thread::spawn(|| {
-                TASK_ID.with(|id| {
-                    for task in receiver {
-                        // Store the task ID into the thread-local before running.
-                        id.set(Some(*task.tag()));
-
-                        // Ignore panics for simplicity.
-                        let _ignore_panic = catch_unwind(|| task.run());
-                    }
-                })
-            });
-
-            sender
-        };
-
-        // A counter that assigns IDs to spawned tasks.
-        static ref COUNTER: AtomicCell<usize> = AtomicCell::new(0);
-    }
-
-    // Reserve an ID for the new task.
-    let id = TaskId(COUNTER.fetch_add(1));
-
-    // Create a task that is scheduled by sending itself into the channel.
-    let schedule = |task| QUEUE.send(task).unwrap();
-    let (task, handle) = async_task::spawn(future, schedule, id);
-
-    // Schedule the task by sending it into the channel.
-    task.schedule();
-
-    handle
-}
-
-fn main() {
-    let mut handles = vec![];
-
-    // Spawn a bunch of tasks.
-    for _ in 0..10 {
-        handles.push(spawn(async move {
-            println!("Hello from task with {:?}", task_id());
-        }));
-    }
-
-    // Wait for the tasks to finish.
-    for handle in handles {
-        executor::block_on(handle);
-    }
-}
diff --git a/src/header.rs b/src/header.rs
index a559e48..82fcf7c 100644
--- a/src/header.rs
+++ b/src/header.rs
@@ -1,4 +1,3 @@
-use core::alloc::Layout;
 use core::cell::UnsafeCell;
 use core::fmt;
 use core::sync::atomic::{AtomicUsize, Ordering};
@@ -6,7 +5,7 @@
 
 use crate::raw::TaskVTable;
 use crate::state::*;
-use crate::utils::{abort_on_panic, extend};
+use crate::utils::abort_on_panic;
 
 /// The header of a task.
 ///
@@ -158,15 +157,6 @@
             abort_on_panic(|| w.wake());
         }
     }
-
-    /// Returns the offset at which the tag of type `T` is stored.
-    #[inline]
-    pub(crate) fn offset_tag<T>() -> usize {
-        let layout_header = Layout::new::<Header>();
-        let layout_t = Layout::new::<T>();
-        let (_, offset_t) = extend(layout_header, layout_t);
-        offset_t
-    }
 }
 
 impl fmt::Debug for Header {
diff --git a/src/join_handle.rs b/src/join_handle.rs
index edf9fcd..e6dbdb8 100644
--- a/src/join_handle.rs
+++ b/src/join_handle.rs
@@ -15,20 +15,20 @@
 ///
 /// * `None` indicates the task has panicked or was canceled.
 /// * `Some(result)` indicates the task has completed with `result` of type `R`.
-pub struct JoinHandle<R, T> {
+pub struct JoinHandle<R> {
     /// A raw task pointer.
     pub(crate) raw_task: NonNull<()>,
 
-    /// A marker capturing generic types `R` and `T`.
-    pub(crate) _marker: PhantomData<(R, T)>,
+    /// A marker capturing generic type `R`.
+    pub(crate) _marker: PhantomData<R>,
 }
 
-unsafe impl<R: Send, T> Send for JoinHandle<R, T> {}
-unsafe impl<R, T> Sync for JoinHandle<R, T> {}
+unsafe impl<R: Send> Send for JoinHandle<R> {}
+unsafe impl<R> Sync for JoinHandle<R> {}
 
-impl<R, T> Unpin for JoinHandle<R, T> {}
+impl<R> Unpin for JoinHandle<R> {}
 
-impl<R, T> JoinHandle<R, T> {
+impl<R> JoinHandle<R> {
     /// Cancels the task.
     ///
     /// If the task has already completed, calling this method will have no effect.
@@ -81,17 +81,6 @@
         }
     }
 
-    /// Returns a reference to the tag stored inside the task.
-    pub fn tag(&self) -> &T {
-        let offset = Header::offset_tag::<T>();
-        let ptr = self.raw_task.as_ptr();
-
-        unsafe {
-            let raw = (ptr as *mut u8).add(offset) as *const T;
-            &*raw
-        }
-    }
-
     /// Returns a waker associated with the task.
     pub fn waker(&self) -> Waker {
         let ptr = self.raw_task.as_ptr();
@@ -104,7 +93,7 @@
     }
 }
 
-impl<R, T> Drop for JoinHandle<R, T> {
+impl<R> Drop for JoinHandle<R> {
     fn drop(&mut self) {
         let ptr = self.raw_task.as_ptr();
         let header = ptr as *const Header;
@@ -185,7 +174,7 @@
     }
 }
 
-impl<R, T> Future for JoinHandle<R, T> {
+impl<R> Future for JoinHandle<R> {
     type Output = Option<R>;
 
     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
@@ -266,7 +255,7 @@
     }
 }
 
-impl<R, T> fmt::Debug for JoinHandle<R, T> {
+impl<R> fmt::Debug for JoinHandle<R> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let ptr = self.raw_task.as_ptr();
         let header = ptr as *const Header;
diff --git a/src/lib.rs b/src/lib.rs
index 3e3cbf6..94afa65 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -39,9 +39,6 @@
 //! task.schedule();
 //! ```
 //!
-//! The last argument to the [`spawn`] function is a *tag*, an arbitrary piece of data associated
-//! with the task. In most executors, this is typically a task identifier or task-local storage.
-//!
 //! The function returns a runnable [`Task`] and a [`JoinHandle`] that can await the result.
 //!
 //! # Execution
diff --git a/src/raw.rs b/src/raw.rs
index 05891d7..0ac0631 100644
--- a/src/raw.rs
+++ b/src/raw.rs
@@ -1,7 +1,6 @@
 use alloc::alloc::Layout;
 use core::cell::UnsafeCell;
 use core::future::Future;
-use core::marker::PhantomData;
 use core::mem::{self, ManuallyDrop};
 use core::pin::Pin;
 use core::ptr::NonNull;
@@ -48,9 +47,6 @@
     /// Memory layout of the whole task.
     pub(crate) layout: Layout,
 
-    /// Offset into the task at which the tag is stored.
-    pub(crate) offset_t: usize,
-
     /// Offset into the task at which the schedule function is stored.
     pub(crate) offset_s: usize,
 
@@ -62,16 +58,13 @@
 }
 
 /// Raw pointers to the fields inside a task.
-pub(crate) struct RawTask<F, R, S, T> {
+pub(crate) struct RawTask<F, R, S> {
     /// The task header.
     pub(crate) header: *const Header,
 
     /// The schedule function.
     pub(crate) schedule: *const S,
 
-    /// The tag inside the task.
-    pub(crate) tag: *mut T,
-
     /// The future.
     pub(crate) future: *mut F,
 
@@ -79,18 +72,18 @@
     pub(crate) output: *mut R,
 }
 
-impl<F, R, S, T> Copy for RawTask<F, R, S, T> {}
+impl<F, R, S> Copy for RawTask<F, R, S> {}
 
-impl<F, R, S, T> Clone for RawTask<F, R, S, T> {
+impl<F, R, S> Clone for RawTask<F, R, S> {
     fn clone(&self) -> Self {
         *self
     }
 }
 
-impl<F, R, S, T> RawTask<F, R, S, T>
+impl<F, R, S> RawTask<F, R, S>
 where
     F: Future<Output = R> + 'static,
-    S: Fn(Task<T>) + Send + Sync + 'static,
+    S: Fn(Task) + Send + Sync + 'static,
 {
     const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
         Self::clone_waker,
@@ -102,7 +95,7 @@
     /// Allocates a task with the given `future` and `schedule` function.
     ///
     /// It is assumed that initially only the `Task` reference and the `JoinHandle` exist.
-    pub(crate) fn allocate(future: F, schedule: S, tag: T) -> NonNull<()> {
+    pub(crate) fn allocate(future: F, schedule: S) -> NonNull<()> {
         // Compute the layout of the task for allocation. Abort if the computation fails.
         let task_layout = abort_on_panic(|| Self::task_layout());
 
@@ -130,9 +123,6 @@
                 },
             });
 
-            // Write the tag as the second field of the task.
-            (raw.tag as *mut T).write(tag);
-
             // Write the schedule function as the third field of the task.
             (raw.schedule as *mut S).write(schedule);
 
@@ -152,7 +142,6 @@
         unsafe {
             Self {
                 header: p as *const Header,
-                tag: p.add(task_layout.offset_t) as *mut T,
                 schedule: p.add(task_layout.offset_s) as *const S,
                 future: p.add(task_layout.offset_f) as *mut F,
                 output: p.add(task_layout.offset_r) as *mut R,
@@ -163,9 +152,8 @@
     /// Returns the memory layout for a task.
     #[inline]
     fn task_layout() -> TaskLayout {
-        // Compute the layouts for `Header`, `T`, `S`, `F`, and `R`.
+        // Compute the layouts for `Header`, `S`, `F`, and `R`.
         let layout_header = Layout::new::<Header>();
-        let layout_t = Layout::new::<T>();
         let layout_s = Layout::new::<S>();
         let layout_f = Layout::new::<F>();
         let layout_r = Layout::new::<R>();
@@ -175,9 +163,8 @@
         let align_union = layout_f.align().max(layout_r.align());
         let layout_union = unsafe { Layout::from_size_align_unchecked(size_union, align_union) };
 
-        // Compute the layout for `Header` followed by `T`, then `S`, and finally `union { F, R }`.
+        // Compute the layout for `Header` followed `S` and `union { F, R }`.
         let layout = layout_header;
-        let (layout, offset_t) = extend(layout, layout_t);
         let (layout, offset_s) = extend(layout, layout_s);
         let (layout, offset_union) = extend(layout, layout_union);
         let offset_f = offset_union;
@@ -185,7 +172,6 @@
 
         TaskLayout {
             layout,
-            offset_t,
             offset_s,
             offset_f,
             offset_r,
@@ -311,7 +297,6 @@
                             // still alive.
                             let task = Task {
                                 raw_task: NonNull::new_unchecked(ptr as *mut ()),
-                                _marker: PhantomData,
                             };
                             (*raw.schedule)(task);
                         }
@@ -403,7 +388,6 @@
 
         let task = Task {
             raw_task: NonNull::new_unchecked(ptr as *mut ()),
-            _marker: PhantomData,
         };
         (*raw.schedule)(task);
     }
@@ -427,7 +411,7 @@
 
     /// Cleans up task's resources and deallocates it.
     ///
-    /// The schedule function and the tag will be dropped, and the task will then get deallocated.
+    /// The schedule function will be dropped, and the task will then get deallocated.
     /// The task must be closed before this function is called.
     #[inline]
     unsafe fn destroy(ptr: *const ()) {
@@ -438,9 +422,6 @@
         abort_on_panic(|| {
             // Drop the schedule function.
             (raw.schedule as *mut S).drop_in_place();
-
-            // Drop the tag.
-            (raw.tag as *mut T).drop_in_place();
         });
 
         // Finally, deallocate the memory reserved by the task.
@@ -609,15 +590,15 @@
         return false;
 
         /// A guard that closes the task if polling its future panics.
-        struct Guard<F, R, S, T>(RawTask<F, R, S, T>)
+        struct Guard<F, R, S>(RawTask<F, R, S>)
         where
             F: Future<Output = R> + 'static,
-            S: Fn(Task<T>) + Send + Sync + 'static;
+            S: Fn(Task) + Send + Sync + 'static;
 
-        impl<F, R, S, T> Drop for Guard<F, R, S, T>
+        impl<F, R, S> Drop for Guard<F, R, S>
         where
             F: Future<Output = R> + 'static,
-            S: Fn(Task<T>) + Send + Sync + 'static,
+            S: Fn(Task) + Send + Sync + 'static,
         {
             fn drop(&mut self) {
                 let raw = self.0;
@@ -632,7 +613,7 @@
                         if state & CLOSED != 0 {
                             // The thread that closed the task didn't drop the future because it
                             // was running so now it's our responsibility to do so.
-                            RawTask::<F, R, S, T>::drop_future(ptr);
+                            RawTask::<F, R, S>::drop_future(ptr);
 
                             // Mark the task as not running and not scheduled.
                             (*raw.header)
@@ -645,7 +626,7 @@
                             }
 
                             // Drop the task reference.
-                            RawTask::<F, R, S, T>::drop_task(ptr);
+                            RawTask::<F, R, S>::drop_task(ptr);
                             break;
                         }
 
@@ -658,7 +639,7 @@
                         ) {
                             Ok(state) => {
                                 // Drop the future because the task is now closed.
-                                RawTask::<F, R, S, T>::drop_future(ptr);
+                                RawTask::<F, R, S>::drop_future(ptr);
 
                                 // Notify the awaiter that the future has been dropped.
                                 if state & AWAITER != 0 {
@@ -666,7 +647,7 @@
                                 }
 
                                 // Drop the task reference.
-                                RawTask::<F, R, S, T>::drop_task(ptr);
+                                RawTask::<F, R, S>::drop_task(ptr);
                                 break;
                             }
                             Err(s) => state = s,
diff --git a/src/task.rs b/src/task.rs
index 4d60f07..6fb4cad 100644
--- a/src/task.rs
+++ b/src/task.rs
@@ -17,7 +17,7 @@
 /// awaits its result.
 ///
 /// When run, the task polls `future`. When woken up, it gets scheduled for running by the
-/// `schedule` function. Argument `tag` is an arbitrary piece of data stored inside the task.
+/// `schedule` function.
 ///
 /// The schedule function should not attempt to run the task nor to drop it. Instead, it should
 /// push the task into some kind of queue so that it can be processed later.
@@ -47,24 +47,22 @@
 /// // Create a task with the future and the schedule function.
 /// let (task, handle) = async_task::spawn(future, schedule, ());
 /// ```
-pub fn spawn<F, R, S, T>(future: F, schedule: S, tag: T) -> (Task<T>, JoinHandle<R, T>)
+pub fn spawn<F, R, S>(future: F, schedule: S) -> (Task, JoinHandle<R>)
 where
     F: Future<Output = R> + Send + 'static,
     R: Send + 'static,
-    S: Fn(Task<T>) + Send + Sync + 'static,
-    T: Send + Sync + 'static,
+    S: Fn(Task) + Send + Sync + 'static,
 {
     // Allocate large futures on the heap.
     let raw_task = if mem::size_of::<F>() >= 2048 {
         let future = alloc::boxed::Box::pin(future);
-        RawTask::<_, R, S, T>::allocate(future, schedule, tag)
+        RawTask::<_, R, S>::allocate(future, schedule)
     } else {
-        RawTask::<F, R, S, T>::allocate(future, schedule, tag)
+        RawTask::<F, R, S>::allocate(future, schedule)
     };
 
     let task = Task {
         raw_task,
-        _marker: PhantomData,
     };
     let handle = JoinHandle {
         raw_task,
@@ -79,7 +77,7 @@
 /// awaits its result.
 ///
 /// When run, the task polls `future`. When woken up, it gets scheduled for running by the
-/// `schedule` function. Argument `tag` is an arbitrary piece of data stored inside the task.
+/// `schedule` function.
 ///
 /// The schedule function should not attempt to run the task nor to drop it. Instead, it should
 /// push the task into some kind of queue so that it can be processed later.
@@ -113,12 +111,11 @@
 /// let (task, handle) = async_task::spawn_local(future, schedule, ());
 /// ```
 #[cfg(feature = "std")]
-pub fn spawn_local<F, R, S, T>(future: F, schedule: S, tag: T) -> (Task<T>, JoinHandle<R, T>)
+pub fn spawn_local<F, R, S>(future: F, schedule: S) -> (Task, JoinHandle<R>)
 where
     F: Future<Output = R> + 'static,
     R: 'static,
-    S: Fn(Task<T>) + Send + Sync + 'static,
-    T: Send + Sync + 'static,
+    S: Fn(Task) + Send + Sync + 'static,
 {
     extern crate std;
 
@@ -175,14 +172,13 @@
     // Allocate large futures on the heap.
     let raw_task = if mem::size_of::<F>() >= 2048 {
         let future = alloc::boxed::Box::pin(future);
-        RawTask::<_, R, S, T>::allocate(future, schedule, tag)
+        RawTask::<_, R, S>::allocate(future, schedule)
     } else {
-        RawTask::<_, R, S, T>::allocate(future, schedule, tag)
+        RawTask::<_, R, S>::allocate(future, schedule)
     };
 
     let task = Task {
         raw_task,
-        _marker: PhantomData,
     };
     let handle = JoinHandle {
         raw_task,
@@ -211,18 +207,15 @@
 /// [`JoinHandle`]: struct.JoinHandle.html
 /// [`Task`]: struct.Task.html
 /// [`Waker`]: https://doc.rust-lang.org/std/task/struct.Waker.html
-pub struct Task<T> {
+pub struct Task {
     /// A pointer to the heap-allocated task.
     pub(crate) raw_task: NonNull<()>,
-
-    /// A marker capturing the generic type `T`.
-    pub(crate) _marker: PhantomData<T>,
 }
 
-unsafe impl<T> Send for Task<T> {}
-unsafe impl<T> Sync for Task<T> {}
+unsafe impl Send for Task {}
+unsafe impl Sync for Task {}
 
-impl<T> Task<T> {
+impl Task {
     /// Schedules the task.
     ///
     /// This is a convenience method that simply reschedules the task by passing it to its schedule
@@ -280,38 +273,21 @@
         }
     }
 
-    /// Returns a reference to the tag stored inside the task.
-    pub fn tag(&self) -> &T {
-        let offset = Header::offset_tag::<T>();
-        let ptr = self.raw_task.as_ptr();
-
-        unsafe {
-            let raw = (ptr as *mut u8).add(offset) as *const T;
-            &*raw
-        }
-    }
-
-    /// Converts this task into a raw pointer to the tag.
-    pub fn into_raw(self) -> *const T {
-        let offset = Header::offset_tag::<T>();
+    /// Converts this task into a raw pointer.
+    pub fn into_raw(self) -> *mut () {
         let ptr = self.raw_task.as_ptr();
         mem::forget(self);
-
-        unsafe { (ptr as *mut u8).add(offset) as *const T }
+        ptr
     }
 
-    /// Converts a raw pointer to the tag into a task.
+    /// Converts a raw pointer into a task.
     ///
     /// This method should only be used with raw pointers returned from [`into_raw`].
     ///
     /// [`into_raw`]: #method.into_raw
-    pub unsafe fn from_raw(raw: *const T) -> Task<T> {
-        let offset = Header::offset_tag::<T>();
-        let ptr = (raw as *mut u8).sub(offset) as *mut ();
-
+    pub unsafe fn from_raw(raw: *mut ()) -> Task {
         Task {
-            raw_task: NonNull::new_unchecked(ptr),
-            _marker: PhantomData,
+            raw_task: NonNull::new_unchecked(raw as *mut ()),
         }
     }
 
@@ -327,7 +303,7 @@
     }
 }
 
-impl<T> Drop for Task<T> {
+impl Drop for Task {
     fn drop(&mut self) {
         let ptr = self.raw_task.as_ptr();
         let header = ptr as *const Header;
@@ -353,14 +329,13 @@
     }
 }
 
-impl<T: fmt::Debug> fmt::Debug for Task<T> {
+impl fmt::Debug for Task {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let ptr = self.raw_task.as_ptr();
         let header = ptr as *const Header;
 
         f.debug_struct("Task")
             .field("header", unsafe { &(*header) })
-            .field("tag", self.tag())
             .finish()
     }
 }
diff --git a/tests/basic.rs b/tests/basic.rs
index 0c0a10f..818f5f5 100644
--- a/tests/basic.rs
+++ b/tests/basic.rs
@@ -78,177 +78,135 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 #[test]
 fn cancel_and_drop_handle() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     task.cancel();
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     drop(handle);
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     drop(task);
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn run_and_drop_handle() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     drop(handle);
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     task.run();
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn drop_handle_and_run() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     drop(handle);
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     task.run();
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn cancel_and_run() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     handle.cancel();
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     drop(handle);
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     task.run();
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn run_and_cancel() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     handle.cancel();
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     drop(handle);
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn cancel_and_poll() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     handle.cancel();
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     let mut handle = handle;
     assert!((&mut handle).now_or_never().is_none());
@@ -258,32 +216,25 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     assert!((&mut handle).now_or_never().is_some());
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     drop(handle);
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn schedule() {
     let (s, r) = channel::unbounded();
     let schedule = move |t| s.send(t).unwrap();
-    let (task, _handle) = async_task::spawn(
-        future::poll_fn(|_| Poll::<()>::Pending),
-        schedule,
-        Box::new(0),
-    );
+    let (task, _handle) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule);
 
     assert!(r.is_empty());
     task.schedule();
@@ -300,52 +251,20 @@
 }
 
 #[test]
-fn tag() {
-    let (s, r) = channel::unbounded();
-    let schedule = move |t| s.send(t).unwrap();
-    let (task, handle) = async_task::spawn(
-        future::poll_fn(|_| Poll::<()>::Pending),
-        schedule,
-        AtomicUsize::new(7),
-    );
-
-    assert!(r.is_empty());
-    task.schedule();
-
-    let task = r.recv().unwrap();
-    assert!(r.is_empty());
-    handle.tag().fetch_add(1, Ordering::SeqCst);
-    task.schedule();
-
-    let task = r.recv().unwrap();
-    assert_eq!(task.tag().load(Ordering::SeqCst), 8);
-    assert!(r.is_empty());
-    task.schedule();
-
-    r.recv().unwrap();
-}
-
-#[test]
 fn schedule_counter() {
+    static COUNT: AtomicUsize = AtomicUsize::new(0);
+
     let (s, r) = channel::unbounded();
-    let schedule = move |t: Task<AtomicUsize>| {
-        t.tag().fetch_add(1, Ordering::SeqCst);
+    let schedule = move |t: Task| {
+        COUNT.fetch_add(1, Ordering::SeqCst);
         s.send(t).unwrap();
     };
-    let (task, handle) = async_task::spawn(
-        future::poll_fn(|_| Poll::<()>::Pending),
-        schedule,
-        AtomicUsize::new(0),
-    );
+    let (task, _handle) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule);
     task.schedule();
 
-    assert_eq!(handle.tag().load(Ordering::SeqCst), 1);
     r.recv().unwrap().schedule();
-
-    assert_eq!(handle.tag().load(Ordering::SeqCst), 2);
     r.recv().unwrap().schedule();
-
-    assert_eq!(handle.tag().load(Ordering::SeqCst), 3);
+    assert_eq!(COUNT.load(Ordering::SeqCst), 3);
     r.recv().unwrap();
 }
 
@@ -359,15 +278,11 @@
     }
     let guard = DropGuard(AtomicUsize::new(0));
 
-    let (task, _) = async_task::spawn(
-        async {},
-        move |task| {
-            assert_eq!(guard.0.load(Ordering::SeqCst), 0);
-            drop(task);
-            assert_eq!(guard.0.load(Ordering::SeqCst), 0);
-        },
-        (),
-    );
+    let (task, _) = async_task::spawn(async {}, move |task| {
+        assert_eq!(guard.0.load(Ordering::SeqCst), 0);
+        drop(task);
+        assert_eq!(guard.0.load(Ordering::SeqCst), 0);
+    });
     task.schedule();
 }
 
@@ -375,11 +290,7 @@
 fn waker() {
     let (s, r) = channel::unbounded();
     let schedule = move |t| s.send(t).unwrap();
-    let (task, handle) = async_task::spawn(
-        future::poll_fn(|_| Poll::<()>::Pending),
-        schedule,
-        Box::new(0),
-    );
+    let (task, handle) = async_task::spawn(future::poll_fn(|_| Poll::<()>::Pending), schedule);
 
     assert!(r.is_empty());
     let w = task.waker();
@@ -395,14 +306,9 @@
 
 #[test]
 fn raw() {
-    let (task, _handle) = async_task::spawn(async {}, |_| panic!(), Box::new(AtomicUsize::new(7)));
+    let (task, _handle) = async_task::spawn(async {}, |_| panic!());
 
     let a = task.into_raw();
-    let task = unsafe {
-        (*a).fetch_add(1, Ordering::SeqCst);
-        Task::from_raw(a)
-    };
-
-    assert_eq!(task.tag().load(Ordering::SeqCst), 8);
+    let task = unsafe { Task::from_raw(a) };
     task.run();
 }
diff --git a/tests/join.rs b/tests/join.rs
index 8e17b34..e7f0972 100644
--- a/tests/join.rs
+++ b/tests/join.rs
@@ -82,7 +82,7 @@
             }
 
             let guard = Guard(Box::new(0));
-            move |task: Task<_>| {
+            move |task: Task| {
                 &guard;
                 task.schedule();
                 $sched.fetch_add(1);
@@ -91,33 +91,6 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 fn ms(ms: u64) -> Duration {
     Duration::from_millis(ms)
 }
@@ -126,7 +99,7 @@
 fn cancel_and_join() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     assert_eq!(DROP_O.load(), 0);
 
@@ -139,7 +112,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(DROP_O.load(), 0);
 }
 
@@ -147,7 +119,7 @@
 fn run_and_join() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     assert_eq!(DROP_O.load(), 0);
 
@@ -159,7 +131,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(DROP_O.load(), 1);
 }
 
@@ -167,7 +138,7 @@
 fn drop_handle_and_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     assert_eq!(DROP_O.load(), 0);
 
@@ -179,7 +150,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(DROP_O.load(), 1);
 }
 
@@ -187,7 +157,7 @@
 fn join_twice() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     assert_eq!(DROP_O.load(), 0);
 
@@ -199,7 +169,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(DROP_O.load(), 1);
 
     assert!(block_on(&mut handle).is_none());
@@ -207,19 +176,17 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(DROP_O.load(), 1);
 
     drop(handle);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn join_and_cancel() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -234,7 +201,6 @@
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_O.load(), 0);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         assert!(block_on(handle).is_none());
@@ -245,7 +211,6 @@
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_O.load(), 0);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
     })
     .unwrap();
 }
@@ -254,7 +219,7 @@
 fn join_and_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -267,7 +232,6 @@
 
             thread::sleep(ms(200));
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         assert!(block_on(handle).is_some());
@@ -278,7 +242,6 @@
 
         thread::sleep(ms(200));
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
     })
     .unwrap();
 }
@@ -287,7 +250,7 @@
 fn try_join_and_run_and_join() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -300,7 +263,6 @@
 
             thread::sleep(ms(200));
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         block_on(future::select(&mut handle, future::ready(())));
@@ -308,7 +270,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
 
         assert!(block_on(handle).is_some());
@@ -319,7 +280,6 @@
 
         thread::sleep(ms(200));
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
     })
     .unwrap();
 }
@@ -328,7 +288,7 @@
 fn try_join_and_cancel_and_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -339,7 +299,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         block_on(future::select(&mut handle, future::ready(())));
@@ -347,7 +306,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
 
         handle.cancel();
@@ -355,7 +313,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
 
         drop(handle);
@@ -363,7 +320,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
     })
     .unwrap();
@@ -373,7 +329,7 @@
 fn try_join_and_run_and_cancel() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -384,7 +340,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 0);
-            assert_eq!(DROP_T.load(), 0);
         });
 
         block_on(future::select(&mut handle, future::ready(())));
@@ -392,7 +347,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
 
         thread::sleep(ms(400));
@@ -402,7 +356,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
 
         drop(handle);
@@ -410,7 +363,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(DROP_O.load(), 1);
     })
     .unwrap();
@@ -435,18 +387,18 @@
     }
 
     for i in 0..10 {
-        let (task, handle) = async_task::spawn(Fut::new(i), drop, Box::new(0));
+        let (task, handle) = async_task::spawn(Fut::new(i), drop);
         task.run();
         assert_eq!(block_on(handle), Some(i));
     }
 
     for i in 0..10 {
-        let (task, handle) = async_task::spawn(Fut::new(vec![7; i]), drop, Box::new(0));
+        let (task, handle) = async_task::spawn(Fut::new(vec![7; i]), drop);
         task.run();
         assert_eq!(block_on(handle), Some(vec![7; i]));
     }
 
-    let (task, handle) = async_task::spawn(Fut::new("foo".to_string()), drop, Box::new(0));
+    let (task, handle) = async_task::spawn(Fut::new("foo".to_string()), drop);
     task.run();
     assert_eq!(block_on(handle), Some("foo".to_string()));
 }
diff --git a/tests/panic.rs b/tests/panic.rs
index 80a8f1b..19c5f2b 100644
--- a/tests/panic.rs
+++ b/tests/panic.rs
@@ -73,7 +73,7 @@
             }
 
             let guard = Guard(Box::new(0));
-            move |_task: Task<_>| {
+            move |_task: Task| {
                 &guard;
                 $sched.fetch_add(1);
             }
@@ -81,33 +81,6 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 fn ms(ms: u64) -> Duration {
     Duration::from_millis(ms)
 }
@@ -116,7 +89,7 @@
 fn cancel_during_run() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -125,7 +98,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         thread::sleep(ms(200));
@@ -135,14 +107,12 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
 
         drop(handle);
         assert_eq!(POLL.load(), 1);
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
     })
     .unwrap();
 }
@@ -151,56 +121,51 @@
 fn run_and_join() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     assert!(catch_unwind(|| task.run()).is_err());
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     assert!(block_on(handle).is_none());
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn try_join_and_run_and_join() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     block_on(future::select(&mut handle, future::ready(())));
     assert_eq!(POLL.load(), 0);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     assert!(catch_unwind(|| task.run()).is_err());
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     assert!(block_on(handle).is_none());
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn join_during_run() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -211,7 +176,6 @@
 
             thread::sleep(ms(200));
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         thread::sleep(ms(200));
@@ -223,7 +187,6 @@
 
         thread::sleep(ms(200));
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
     })
     .unwrap();
 }
@@ -232,7 +195,7 @@
 fn try_join_during_run() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -241,7 +204,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         thread::sleep(ms(200));
@@ -251,7 +213,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         drop(handle);
     })
     .unwrap();
@@ -261,7 +222,7 @@
 fn drop_handle_during_run() {
     future!(f, POLL, DROP_F);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -270,7 +231,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         thread::sleep(ms(200));
@@ -280,7 +240,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
     })
     .unwrap();
 }
diff --git a/tests/ready.rs b/tests/ready.rs
index a966760..5f0efe1 100644
--- a/tests/ready.rs
+++ b/tests/ready.rs
@@ -82,7 +82,7 @@
             }
 
             let guard = Guard(Box::new(0));
-            move |_task: Task<_>| {
+            move |_task: Task| {
                 &guard;
                 $sched.fetch_add(1);
             }
@@ -90,33 +90,6 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 fn ms(ms: u64) -> Duration {
     Duration::from_millis(ms)
 }
@@ -125,7 +98,7 @@
 fn cancel_during_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -134,7 +107,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 0);
-            assert_eq!(DROP_T.load(), 0);
             assert_eq!(DROP_O.load(), 1);
         });
 
@@ -145,7 +117,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
 
         thread::sleep(ms(400));
@@ -154,7 +125,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 1);
 
         drop(handle);
@@ -162,7 +132,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(DROP_O.load(), 1);
     })
     .unwrap();
@@ -172,7 +141,7 @@
 fn join_during_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -183,7 +152,6 @@
 
             thread::sleep(ms(200));
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
         });
 
         thread::sleep(ms(200));
@@ -196,7 +164,6 @@
 
         thread::sleep(ms(200));
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
     })
     .unwrap();
 }
@@ -205,7 +172,7 @@
 fn try_join_during_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, mut handle, f, s, DROP_T);
+    let (task, mut handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -214,7 +181,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(DROP_O.load(), 1);
         });
 
@@ -225,7 +191,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
         drop(handle);
     })
@@ -236,7 +201,7 @@
 fn drop_handle_during_run() {
     future!(f, POLL, DROP_F, DROP_O);
     schedule!(s, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     crossbeam::scope(|scope| {
         scope.spawn(|_| {
@@ -245,7 +210,6 @@
             assert_eq!(SCHEDULE.load(), 0);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(DROP_O.load(), 1);
         });
 
@@ -256,7 +220,6 @@
         assert_eq!(SCHEDULE.load(), 0);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(DROP_O.load(), 0);
     })
     .unwrap();
diff --git a/tests/waker_fn.rs b/tests/waker_fn.rs
deleted file mode 100644
index fdad34c..0000000
--- a/tests/waker_fn.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-use std::sync::atomic::{AtomicUsize, Ordering};
-use std::sync::Arc;
-
-#[test]
-fn wake() {
-    let a = Arc::new(AtomicUsize::new(0));
-    let w = async_task::waker_fn({
-        let a = a.clone();
-        move || {
-            a.fetch_add(1, Ordering::SeqCst);
-        }
-    });
-
-    assert_eq!(a.load(Ordering::SeqCst), 0);
-    w.wake_by_ref();
-    assert_eq!(a.load(Ordering::SeqCst), 1);
-
-    let w2 = w.clone();
-    assert_eq!(a.load(Ordering::SeqCst), 1);
-    w2.wake_by_ref();
-    assert_eq!(a.load(Ordering::SeqCst), 2);
-    drop(w2);
-    assert_eq!(a.load(Ordering::SeqCst), 2);
-
-    let w3 = w.clone();
-    assert_eq!(a.load(Ordering::SeqCst), 2);
-    w3.wake();
-    assert_eq!(a.load(Ordering::SeqCst), 3);
-}
diff --git a/tests/waker_panic.rs b/tests/waker_panic.rs
index 60b7fd6..b090d00 100644
--- a/tests/waker_panic.rs
+++ b/tests/waker_panic.rs
@@ -92,7 +92,7 @@
             }
 
             let guard = Guard(Box::new(0));
-            let sched = move |task: Task<_>| {
+            let sched = move |task: Task| {
                 &guard;
                 $sched.fetch_add(1);
                 s.send(task).unwrap();
@@ -103,33 +103,6 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 fn ms(ms: u64) -> Duration {
     Duration::from_millis(ms)
 }
@@ -138,7 +111,7 @@
 fn wake_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -153,7 +126,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -165,7 +137,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -174,7 +145,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -184,7 +154,7 @@
 fn cancel_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -199,7 +169,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -210,7 +179,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         drop(handle);
@@ -218,7 +186,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -227,7 +194,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -237,7 +203,7 @@
 fn wake_and_cancel_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -252,7 +218,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -263,7 +228,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         handle.cancel();
@@ -271,7 +235,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         drop(handle);
@@ -279,7 +242,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -288,7 +250,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -298,7 +259,7 @@
 fn cancel_and_wake_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -313,7 +274,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -324,7 +284,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         drop(handle);
@@ -332,7 +291,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         w.wake();
@@ -340,7 +298,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -349,7 +306,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -359,7 +315,7 @@
 fn panic_and_poll() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     waker().wake();
@@ -367,7 +323,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     let mut handle = handle;
     assert!((&mut handle).now_or_never().is_none());
@@ -378,14 +333,12 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     assert!((&mut handle).now_or_never().is_some());
     assert_eq!(POLL.load(), 2);
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
 
     drop(waker());
     drop(handle);
@@ -393,5 +346,4 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
diff --git a/tests/waker_pending.rs b/tests/waker_pending.rs
index 1374c4a..8887264 100644
--- a/tests/waker_pending.rs
+++ b/tests/waker_pending.rs
@@ -81,7 +81,7 @@
             }
 
             let guard = Guard(Box::new(0));
-            let sched = move |task: Task<_>| {
+            let sched = move |task: Task| {
                 &guard;
                 $sched.fetch_add(1);
                 s.send(task).unwrap();
@@ -92,33 +92,6 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 fn ms(ms: u64) -> Duration {
     Duration::from_millis(ms)
 }
@@ -127,7 +100,7 @@
 fn wake_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, _handle, f, s, DROP_T);
+    let (task, _handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -141,7 +114,6 @@
             assert_eq!(SCHEDULE.load(), 2);
             assert_eq!(DROP_F.load(), 0);
             assert_eq!(DROP_S.load(), 0);
-            assert_eq!(DROP_T.load(), 0);
             assert_eq!(chan.len(), 1);
         });
 
@@ -152,7 +124,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -161,7 +132,6 @@
         assert_eq!(SCHEDULE.load(), 2);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 1);
     })
     .unwrap();
@@ -174,7 +144,7 @@
 fn cancel_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -189,7 +159,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -200,7 +169,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         drop(handle);
@@ -208,7 +176,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -217,7 +184,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -227,7 +193,7 @@
 fn wake_and_cancel_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -242,7 +208,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -253,7 +218,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         handle.cancel();
@@ -261,7 +225,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         drop(handle);
@@ -269,7 +232,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -278,7 +240,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -288,7 +249,7 @@
 fn cancel_and_wake_during_run() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -303,7 +264,6 @@
             assert_eq!(SCHEDULE.load(), 1);
             assert_eq!(DROP_F.load(), 1);
             assert_eq!(DROP_S.load(), 1);
-            assert_eq!(DROP_T.load(), 1);
             assert_eq!(chan.len(), 0);
         });
 
@@ -314,7 +274,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         drop(handle);
@@ -322,7 +281,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         w.wake();
@@ -330,7 +288,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 0);
         assert_eq!(DROP_S.load(), 0);
-        assert_eq!(DROP_T.load(), 0);
         assert_eq!(chan.len(), 0);
 
         thread::sleep(ms(400));
@@ -339,7 +296,6 @@
         assert_eq!(SCHEDULE.load(), 1);
         assert_eq!(DROP_F.load(), 1);
         assert_eq!(DROP_S.load(), 1);
-        assert_eq!(DROP_T.load(), 1);
         assert_eq!(chan.len(), 0);
     })
     .unwrap();
@@ -349,7 +305,7 @@
 fn drop_last_waker() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -359,7 +315,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     drop(w);
@@ -367,7 +322,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 1);
 
     chan.recv().unwrap().run();
@@ -375,7 +329,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }
 
@@ -383,7 +336,7 @@
 fn cancel_last_handle() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     drop(waker());
@@ -391,7 +344,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     handle.cancel();
@@ -399,7 +351,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 1);
 
     chan.recv().unwrap().run();
@@ -407,7 +358,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     drop(handle);
@@ -415,7 +365,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }
 
@@ -423,7 +372,7 @@
 fn drop_last_handle() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, handle, f, s, DROP_T);
+    let (task, handle) = async_task::spawn(f, s);
 
     task.run();
     drop(waker());
@@ -431,7 +380,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     drop(handle);
@@ -439,7 +387,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 1);
 
     chan.recv().unwrap().run();
@@ -447,6 +394,5 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }
diff --git a/tests/waker_ready.rs b/tests/waker_ready.rs
index 7d1d59e..bf92960 100644
--- a/tests/waker_ready.rs
+++ b/tests/waker_ready.rs
@@ -90,7 +90,7 @@
             }
 
             let guard = Guard(Box::new(0));
-            let sched = move |task: Task<_>| {
+            let sched = move |task: Task| {
                 &guard;
                 $sched.fetch_add(1);
                 s.send(task).unwrap();
@@ -101,33 +101,6 @@
     };
 }
 
-// Creates a task with event counters.
-//
-// Usage: `task!(task, handle f, s, DROP)`
-//
-// A task with future `f` and schedule function `s` is created.
-// The `Task` and `JoinHandle` are bound to `task` and `handle`, respectively.
-// When the tag inside the task gets dropped, `DROP` is incremented.
-macro_rules! task {
-    ($task:pat, $handle: pat, $future:expr, $schedule:expr, $drop:ident) => {
-        lazy_static! {
-            static ref $drop: AtomicCell<usize> = AtomicCell::new(0);
-        }
-
-        let ($task, $handle) = {
-            struct Tag(Box<i32>);
-
-            impl Drop for Tag {
-                fn drop(&mut self) {
-                    $drop.fetch_add(1);
-                }
-            }
-
-            async_task::spawn($future, $schedule, Tag(Box::new(0)))
-        };
-    };
-}
-
 fn ms(ms: u64) -> Duration {
     Duration::from_millis(ms)
 }
@@ -136,7 +109,7 @@
 fn wake() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(mut task, _, f, s, DROP_T);
+    let (mut task, _) = async_task::spawn(f, s);
 
     assert!(chan.is_empty());
 
@@ -145,7 +118,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     waker().wake();
@@ -154,7 +126,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     task.run();
@@ -162,7 +133,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     waker().wake();
@@ -170,7 +140,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }
 
@@ -178,7 +147,7 @@
 fn wake_by_ref() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(mut task, _, f, s, DROP_T);
+    let (mut task, _) = async_task::spawn(f, s);
 
     assert!(chan.is_empty());
 
@@ -187,7 +156,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     waker().wake_by_ref();
@@ -196,7 +164,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     task.run();
@@ -204,7 +171,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     waker().wake_by_ref();
@@ -212,7 +178,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }
 
@@ -220,14 +185,13 @@
 fn clone() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(mut task, _, f, s, DROP_T);
+    let (mut task, _) = async_task::spawn(f, s);
 
     task.run();
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     let w2 = waker().clone();
@@ -241,7 +205,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     w3.wake();
@@ -249,27 +212,24 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     drop(w2);
     drop(waker());
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
 }
 
 #[test]
 fn wake_canceled() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, _, f, s, DROP_T);
+    let (task, _) = async_task::spawn(f, s);
 
     task.run();
     assert_eq!(POLL.load(), 1);
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     let w = waker();
@@ -280,7 +240,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     w.wake();
@@ -288,7 +247,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }
 
@@ -296,7 +254,7 @@
 fn wake_completed() {
     future!(f, waker, POLL, DROP_F);
     schedule!(s, chan, SCHEDULE, DROP_S);
-    task!(task, _, f, s, DROP_T);
+    let (task, _) = async_task::spawn(f, s);
 
     task.run();
     let w = waker();
@@ -304,7 +262,6 @@
     assert_eq!(SCHEDULE.load(), 0);
     assert_eq!(DROP_F.load(), 0);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     w.wake();
@@ -313,7 +270,6 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 0);
-    assert_eq!(DROP_T.load(), 0);
     assert_eq!(chan.len(), 0);
 
     waker().wake();
@@ -321,6 +277,5 @@
     assert_eq!(SCHEDULE.load(), 1);
     assert_eq!(DROP_F.load(), 1);
     assert_eq!(DROP_S.load(), 1);
-    assert_eq!(DROP_T.load(), 1);
     assert_eq!(chan.len(), 0);
 }