Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1145
diff -u -p -r1.1145 common.inc
--- includes/common.inc	7 Apr 2010 17:30:43 -0000	1.1145
+++ includes/common.inc	9 Apr 2010 22:54:05 -0000
@@ -4634,8 +4634,14 @@ function drupal_system_listing($mask, $d
  *   (optional) An additional variable that is passed by reference. If more
  *   context needs to be provided to implementations, then this should be an
  *   keyed array as described above.
+ * @param $ids
+ *   (optional) An array of ids for executing specific implementations in
+ *   addition to generic ones. For example, when the Drupal Form API is using
+ *   drupal_alter() to execute both hook_form_alter() and
+ *   hook_form_FORM_ID_alter() implementations, it uses 'form' as $type and
+ *   passes array($form_id) as $ids.
  */
-function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
+function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL, $ids = array()) {
   // Use the advanced drupal_static() pattern, since this is called very often.
   static $drupal_static_fast;
   if (!isset($drupal_static_fast)) {
@@ -4646,11 +4652,40 @@ function drupal_alter($type, &$data, &$c
   // Some alter hooks are invoked many times per page request, so statically
   // cache the list of functions to call, and on subsequent calls, iterate
   // through them quickly.
-  if (!isset($functions[$type])) {
-    $functions[$type] = array();
+  $cid = empty($ids) ? $type : ($type . ':' . implode(',', $ids));
+  if (!isset($functions[$cid])) {
+    $functions[$cid] = array();
     $hook = $type . '_alter';
-    foreach (module_implements($hook) as $module) {
-      $functions[$type][] = $module . '_' . $hook;
+    $modules = module_implements($hook);
+    if (empty($ids)) {
+      foreach ($modules as $module) {
+        $functions[$cid][] = $module . '_' . $hook;
+      }
+    }
+    else {
+      $modules_with_id_implementations = array();
+      foreach ($ids as $id) {
+        $modules_with_id_implementations = array_merge($modules_with_id_implementations, module_implements($type . '_' . $id . '_alter'));
+      }
+      // If any modules implement an id-specific hook that do not implement the
+      // generic hook, we need to add them to the $modules array in their
+      // appropriate order.
+      if (array_diff($modules_with_id_implementations, $modules)) {
+        // Order the modules by the order returned by module_list().
+        $modules = array_intersect(module_list(), array_merge($modules, $modules_with_id_implementations));
+      }
+      foreach ($modules as $module) {
+        $function = $module . '_' . $hook;
+        if (function_exists($function)) {
+          $functions[$cid][] = $function;
+        }
+        foreach ($ids as $id) {
+          $function = $module . '_' . $type . '_' . $id . '_alter';
+          if (function_exists($function)) {
+            $functions[$cid][] = $function;
+          }
+        }
+      }
     }
     // Allow the theme to alter variables after the theme system has been
     // initialized.
@@ -4664,12 +4699,18 @@ function drupal_alter($type, &$data, &$c
       foreach ($theme_keys as $theme_key) {
         $function = $theme_key . '_' . $hook;
         if (function_exists($function)) {
-          $functions[$type][] = $function;
+          $functions[$cid][] = $function;
+        }
+        foreach ($ids as $id) {
+          $function = $theme_key . '_' . $type . '_' . $id . '_alter';
+          if (function_exists($function)) {
+            $functions[$cid][] = $function;
+          }
         }
       }
     }
   }
-  foreach ($functions[$type] as $function) {
+  foreach ($functions[$cid] as $function) {
     $function($data, $context1, $context2);
   }
 }
Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.449
diff -u -p -r1.449 form.inc
--- includes/form.inc	7 Apr 2010 04:39:59 -0000	1.449
+++ includes/form.inc	9 Apr 2010 22:54:05 -0000
@@ -761,11 +761,8 @@ function drupal_prepare_form($form_id, &
     }
   }
 
-  // Invoke hook_form_FORM_ID_alter() implementations.
-  drupal_alter('form_' . $form_id, $form, $form_state);
-
-  // Invoke hook_form_alter() implementations.
-  drupal_alter('form', $form, $form_state, $form_id);
+  // Invoke hook_form_alter() and hook_form_FORM_ID_alter() implementations.
+  drupal_alter('form', $form, $form_state, $form_id, array($form_id));
 }
 
 
Index: includes/database/select.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/database/select.inc,v
retrieving revision 1.34
diff -u -p -r1.34 select.inc
--- includes/database/select.inc	17 Feb 2010 05:24:53 -0000	1.34
+++ includes/database/select.inc	9 Apr 2010 22:54:06 -0000
@@ -1069,10 +1069,8 @@ class SelectQuery extends Query implemen
 
     // Modules may alter all queries or only those having a particular tag.
     if (isset($this->alterTags)) {
-      drupal_alter('query', $query);
-      foreach ($this->alterTags as $tag => $value) {
-        drupal_alter("query_$tag", $query);
-      }
+      $null = NULL;
+      drupal_alter('query', $query, $null, $null, array_keys($this->alterTags));
     }
     return $this->prepared = TRUE;
   }
Index: modules/block/block.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.api.php,v
retrieving revision 1.10
diff -u -p -r1.10 block.api.php
--- modules/block/block.api.php	28 Mar 2010 11:16:29 -0000	1.10
+++ modules/block/block.api.php	9 Apr 2010 22:54:07 -0000
@@ -194,10 +194,6 @@ function hook_block_view_alter(&$data, $
  * Modules can implement hook_block_view_MODULE_DELTA_alter() to modify a
  * specific block, rather than implementing hook_block_view_alter().
  *
- * Note that this hook fires before hook_block_view_alter(). Therefore, all
- * implementations of hook_block_view_MODULE_DELTA_alter() will run before all
- * implementations of hook_block_view_alter(), regardless of the module order.
- *
  * @param $data
  *   An array of data, as returned from the hook_block_view() implementation of
  *   the module that defined the block:
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.415
diff -u -p -r1.415 block.module
--- modules/block/block.module	28 Mar 2010 11:16:29 -0000	1.415
+++ modules/block/block.module	9 Apr 2010 22:54:07 -0000
@@ -740,8 +740,8 @@ function _block_render_blocks($region_bl
 
         // Allow modules to modify the block before it is viewed, via either
         // hook_block_view_MODULE_DELTA_alter() or hook_block_view_alter().
-        drupal_alter("block_view_{$block->module}_{$block->delta}", $array, $block);
-        drupal_alter('block_view', $array, $block);
+        $null = NULL;
+        drupal_alter('block_view', $array, $block, $null, array($block->module . '_' . $block->delta));
 
         if (isset($cid)) {
           cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
Index: modules/simpletest/tests/form.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form.test,v
retrieving revision 1.46
diff -u -p -r1.46 form.test
--- modules/simpletest/tests/form.test	7 Apr 2010 17:30:43 -0000	1.46
+++ modules/simpletest/tests/form.test	9 Apr 2010 22:54:11 -0000
@@ -179,6 +179,40 @@ class FormsTestCase extends DrupalWebTes
 }
 
 /**
+ * Test form alter hooks.
+ */
+class FormAlterTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form alter hooks',
+      'description' => 'Tests hook_form_alter() and hook_form_FORM_ID_alter().',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests execution order of hook_form_alter() and hook_form_FORM_ID_alter().
+   */
+  function testExecutionOrder() {
+    $this->drupalGet('form-test/alter');
+    // Ensure that the order is first by module, then for a given module, the
+    // id-specific one after the generic one.
+    $expected = array(
+      'block_form_form_test_alter_form_alter() executed.',
+      'form_test_form_alter() executed.',
+      'form_test_form_form_test_alter_form_alter() executed.',
+      'system_form_form_test_alter_form_alter() executed.',
+    );
+    $content = preg_replace('/\s+/', ' ', filter_xss($this->content, array()));
+    $this->assert(strpos($content, implode(' ', $expected)) !== FALSE, t('Form alter hooks executed in the expected order.'));
+  }
+}
+
+/**
  * Test form validation handlers.
  */
 class FormValidationTestCase extends DrupalWebTestCase {
Index: modules/simpletest/tests/form_test.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/form_test.module,v
retrieving revision 1.35
diff -u -p -r1.35 form_test.module
--- modules/simpletest/tests/form_test.module	7 Apr 2010 17:30:43 -0000	1.35
+++ modules/simpletest/tests/form_test.module	9 Apr 2010 22:54:11 -0000
@@ -10,6 +10,13 @@
  * Implements hook_menu().
  */
 function form_test_menu() {
+  $items['form-test/alter'] = array(
+    'title' => 'Form altering test',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_alter_form'),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
   $items['form-test/validate'] = array(
     'title' => 'Form validation handlers test',
     'page callback' => 'drupal_get_form',
@@ -142,6 +149,46 @@ function form_test_menu() {
 }
 
 /**
+ * Form builder for testing hook_form_alter() and hook_form_FORM_ID_alter().
+ */
+function form_test_alter_form($form, &$form_state) {
+  // Elements can be added as needed for future testing needs, but for now,
+  // we're only testing alter hooks that do not require any elements added by
+  // this function.
+  return $form;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter() on behalf of block.module.
+ */
+function block_form_form_test_alter_form_alter(&$form, &$form_state) {
+  drupal_set_message(t('block_form_form_test_alter_form_alter() executed.'));
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function form_test_form_alter(&$form, &$form_state, $form_id) {
+  if ($form_id == 'form_test_alter_form') {
+    drupal_set_message(t('form_test_form_alter() executed.'));
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function form_test_form_form_test_alter_form_alter(&$form, &$form_state) {
+  drupal_set_message(t('form_test_form_form_test_alter_form_alter() executed.'));
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter() on behalf of system.module.
+ */
+function system_form_form_test_alter_form_alter(&$form, &$form_state) {
+  drupal_set_message(t('system_form_form_test_alter_form_alter() executed.'));
+}
+
+/**
  * Form builder for testing drupal_validate_form().
  *
  * Serves for testing form processing and alterations by form validation
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.150
diff -u -p -r1.150 system.api.php
--- modules/system/system.api.php	6 Apr 2010 19:49:03 -0000	1.150
+++ modules/system/system.api.php	9 Apr 2010 22:54:11 -0000
@@ -774,10 +774,6 @@ function hook_form_alter(&$form, &$form_
  * rather than implementing hook_form_alter() and checking the form ID, or
  * using long switch statements to alter multiple forms.
  *
- * Note that this hook fires before hook_form_alter(). Therefore all
- * implementations of hook_form_FORM_ID_alter() will run before all implementations
- * of hook_form_alter(), regardless of the module order.
- *
  * @param $form
  *   Nested array of form elements that comprise the form.
  * @param $form_state
