Index: src/wp-admin/includes/plugin.php
===================================================================
--- src/wp-admin/includes/plugin.php	(revision 38659)
+++ src/wp-admin/includes/plugin.php	(working copy)
@@ -1765,140 +1765,6 @@
 /* Whitelist functions */
 
 /**
- * Register a setting and its data.
- *
- * @since 2.7.0
- *
- * @global array $new_whitelist_options
- * @global array $wp_registered_settings
- *
- * @param string $option_group A settings group name. Should correspond to a whitelisted option key name.
- * 	Default whitelisted option key names include "general," "discussion," and "reading," among others.
- * @param string $option_name The name of an option to sanitize and save.
- * @param array  $args {
- *     Data used to describe the setting when registered.
- *
- *     @type string   $type              The type of data associated with this setting.
- *     @type string   $description       A description of the data attached to this setting.
- *     @type callable $sanitize_callback A callback function that sanitizes the option's value.
- *     @type bool     $show_in_rest      Whether data associated with this setting should be included in the REST API.
- * }
- */
-function register_setting( $option_group, $option_name, $args = array() ) {
-	global $new_whitelist_options, $wp_registered_settings;
-
-	$defaults = array(
-		'type'              => 'string',
-		'group'             => $option_group,
-		'description'       => '',
-		'sanitize_callback' => null,
-		'show_in_rest'      => false,
-	);
-
-	// Back-compat: old sanitize callback is added.
-	if ( is_callable( $args ) ) {
-		$args = array(
-			'sanitize_callback' => $args,
-		);
-	}
-
-	/**
-	 * Filters the registration arguments when registering a setting.
-	 *
-	 * @since 4.7.0
-	 *
-	 * @param array  $args         Array of setting registration arguments.
-	 * @param array  $defaults     Array of default arguments.
-	 * @param string $option_group Setting group.
-	 * @param string $option_name  Setting name.
-	 */
-	$args = apply_filters( 'register_setting_args', $args, $defaults, $option_group, $option_name );
-	$args = wp_parse_args( $args, $defaults );
-
-	if ( ! is_array( $wp_registered_settings ) ) {
-		$wp_registered_settings = array();
-	}
-
-	if ( 'misc' == $option_group ) {
-		_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
-		$option_group = 'general';
-	}
-
-	if ( 'privacy' == $option_group ) {
-		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
-		$option_group = 'reading';
-	}
-
-	$new_whitelist_options[ $option_group ][] = $option_name;
-	if ( ! empty( $args['sanitize_callback'] ) ) {
-		add_filter( "sanitize_option_{$option_name}", $args['sanitize_callback'] );
-	}
-
-	$wp_registered_settings[ $option_name ] = $args;
-}
-
-/**
- * Unregister a setting.
- *
- * @since 2.7.0
- * @since 4.7.0 `$sanitize_callback` was deprecated. The callback from `register_setting()` is now used instead.
- *
- * @global array $new_whitelist_options
- *
- * @param string   $option_group      The settings group name used during registration.
- * @param string   $option_name       The name of the option to unregister.
- * @param callable $deprecated        Deprecated.
- */
-function unregister_setting( $option_group, $option_name, $deprecated = '' ) {
-	global $new_whitelist_options, $wp_registered_settings;
-
-	if ( 'misc' == $option_group ) {
-		_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
-		$option_group = 'general';
-	}
-
-	if ( 'privacy' == $option_group ) {
-		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
-		$option_group = 'reading';
-	}
-
-	$pos = array_search( $option_name, (array) $new_whitelist_options[ $option_group ] );
-	if ( $pos !== false ) {
-		unset( $new_whitelist_options[ $option_group ][ $pos ] );
-	}
-	if ( '' !== $deprecated ) {
-		_deprecated_argument( __FUNCTION__, '4.7.0', __( '$sanitize_callback is deprecated. The callback from register_setting() is used instead.' ) );
-		remove_filter( "sanitize_option_{$option_name}", $deprecated );
-	}
-
-	if ( isset( $wp_registered_settings[ $option_name ] ) ) {
-		// Remove the sanitize callback if one was set during registration.
-		if ( ! empty( $wp_registered_settings[ $option_name ]['sanitize_callback'] ) ) {
-			remove_filter( "sanitize_option_{$option_name}", $wp_registered_settings[ $option_name ]['sanitize_callback'] );
-		}
-
-		unset( $wp_registered_settings[ $option_name ] );
-	}
-}
-
-/**
- * Retrieves an array of registered settings.
- *
- * @since 4.7.0
- *
- * @return array List of registered settings, keyed by option name.
- */
-function get_registered_settings() {
-	global $wp_registered_settings;
-
-	if ( ! is_array( $wp_registered_settings ) ) {
-		return array();
-	}
-
-	return $wp_registered_settings;
-}
-
-/**
  * Refreshes the value of the options whitelist available via the 'whitelist_options' hook.
  *
  * See the {@see 'whitelist_options'} filter.
Index: src/wp-includes/option.php
===================================================================
--- src/wp-includes/option.php	(revision 38659)
+++ src/wp-includes/option.php	(working copy)
@@ -56,6 +56,9 @@
 	if ( defined( 'WP_SETUP_CONFIG' ) )
 		return false;
 
+	// Distinguish between `false` as a default, and not passing one.
+	$passed_default = func_num_args() > 1;
+
 	if ( ! wp_installing() ) {
 		// prevent non-existent options from triggering multiple queries
 		$notoptions = wp_cache_get( 'notoptions', 'options' );
@@ -67,12 +70,14 @@
 			 *
 			 * @since 3.4.0
 			 * @since 4.4.0 The `$option` parameter was added.
+			 * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value.
 			 *
 			 * @param mixed  $default The default value to return if the option does not exist
 			 *                        in the database.
 			 * @param string $option  Option name.
+			 * @param bool   $passed_default Was `get_option()` passed a default value?
 			 */
-			return apply_filters( "default_option_{$option}", $default, $option );
+			return apply_filters( "default_option_{$option}", $default, $option, $passed_default );
 		}
 
 		$alloptions = wp_load_alloptions();
@@ -97,7 +102,7 @@
 					wp_cache_set( 'notoptions', $notoptions, 'options' );
 
 					/** This filter is documented in wp-includes/option.php */
-					return apply_filters( 'default_option_' . $option, $default, $option );
+					return apply_filters( 'default_option_' . $option, $default, $option, $passed_default );
 				}
 			}
 		}
@@ -1686,3 +1691,165 @@
 	}
 	return $result;
 }
+
+/**
+ * Register a setting and its data.
+ *
+ * @since 2.7.0
+ *
+ * @global array $new_whitelist_options
+ * @global array $wp_registered_settings
+ *
+ * @param string $option_group A settings group name. Should correspond to a whitelisted option key name.
+ * 	Default whitelisted option key names include "general," "discussion," and "reading," among others.
+ * @param string $option_name The name of an option to sanitize and save.
+ * @param array  $args {
+ *     Data used to describe the setting when registered.
+ *
+ *     @type string   $type              The type of data associated with this setting.
+ *     @type string   $description       A description of the data attached to this setting.
+ *     @type callable $sanitize_callback A callback function that sanitizes the option's value.
+ *     @type bool     $show_in_rest      Whether data associated with this setting should be included in the REST API.
+ *     @type mixed    $default           Default value when calling `get_option()`.
+ * }
+ */
+function register_setting( $option_group, $option_name, $args = array() ) {
+	global $new_whitelist_options, $wp_registered_settings;
+
+	$defaults = array(
+		'type'              => 'string',
+		'group'             => $option_group,
+		'description'       => '',
+		'sanitize_callback' => null,
+		'show_in_rest'      => false,
+	);
+
+	// Back-compat: old sanitize callback is added.
+	if ( is_callable( $args ) ) {
+		$args = array(
+			'sanitize_callback' => $args,
+		);
+	}
+
+	/**
+	 * Filters the registration arguments when registering a setting.
+	 *
+	 * @since 4.7.0
+	 *
+	 * @param array  $args         Array of setting registration arguments.
+	 * @param array  $defaults     Array of default arguments.
+	 * @param string $option_group Setting group.
+	 * @param string $option_name  Setting name.
+	 */
+	$args = apply_filters( 'register_setting_args', $args, $defaults, $option_group, $option_name );
+	$args = wp_parse_args( $args, $defaults );
+
+	if ( ! is_array( $wp_registered_settings ) ) {
+		$wp_registered_settings = array();
+	}
+
+	if ( 'misc' == $option_group ) {
+		_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
+		$option_group = 'general';
+	}
+
+	if ( 'privacy' == $option_group ) {
+		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
+		$option_group = 'reading';
+	}
+
+	$new_whitelist_options[ $option_group ][] = $option_name;
+	if ( ! empty( $args['sanitize_callback'] ) ) {
+		add_filter( "sanitize_option_{$option_name}", $args['sanitize_callback'] );
+	}
+	if ( array_key_exists( 'default', $args ) ) {
+		add_filter( "default_option_{$option_name}", 'filter_default_option', 10, 3 );
+	}
+
+	$wp_registered_settings[ $option_name ] = $args;
+}
+
+/**
+ * Unregister a setting.
+ *
+ * @since 2.7.0
+ * @since 4.7.0 `$sanitize_callback` was deprecated. The callback from `register_setting()` is now used instead.
+ *
+ * @global array $new_whitelist_options
+ *
+ * @param string   $option_group      The settings group name used during registration.
+ * @param string   $option_name       The name of the option to unregister.
+ * @param callable $deprecated        Deprecated.
+ */
+function unregister_setting( $option_group, $option_name, $deprecated = '' ) {
+	global $new_whitelist_options, $wp_registered_settings;
+
+	if ( 'misc' == $option_group ) {
+		_deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'misc' ) );
+		$option_group = 'general';
+	}
+
+	if ( 'privacy' == $option_group ) {
+		_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( 'The "%s" options group has been removed. Use another settings group.' ), 'privacy' ) );
+		$option_group = 'reading';
+	}
+
+	$pos = array_search( $option_name, (array) $new_whitelist_options[ $option_group ] );
+	if ( $pos !== false ) {
+		unset( $new_whitelist_options[ $option_group ][ $pos ] );
+	}
+	if ( '' !== $deprecated ) {
+		_deprecated_argument( __FUNCTION__, '4.7.0', __( '$sanitize_callback is deprecated. The callback from register_setting() is used instead.' ) );
+		remove_filter( "sanitize_option_{$option_name}", $deprecated );
+	}
+
+	if ( isset( $wp_registered_settings[ $option_name ] ) ) {
+		// Remove the sanitize callback if one was set during registration.
+		if ( ! empty( $wp_registered_settings[ $option_name ]['sanitize_callback'] ) ) {
+			remove_filter( "sanitize_option_{$option_name}", $wp_registered_settings[ $option_name ]['sanitize_callback'] );
+		}
+
+		unset( $wp_registered_settings[ $option_name ] );
+	}
+}
+
+/**
+ * Retrieves an array of registered settings.
+ *
+ * @since 4.7.0
+ *
+ * @return array List of registered settings, keyed by option name.
+ */
+function get_registered_settings() {
+	global $wp_registered_settings;
+
+	if ( ! is_array( $wp_registered_settings ) ) {
+		return array();
+	}
+
+	return $wp_registered_settings;
+}
+
+/**
+ * Filter the default value for the option.
+ *
+ * For settings which register a default setting in `register_setting()`, this
+ * function is added as a filter to `default_option_{$option}`.
+ *
+ * @param mixed $default Existing default value to return.
+ * @param string $option Option name.
+ * @param bool $passed_default Was `get_option()` passed a default value?
+ * @return mixed Filtered default value.
+ */
+function filter_default_option( $default, $option, $passed_default ) {
+	if ( $passed_default ) {
+		return $default;
+	}
+
+	$registered = get_registered_settings();
+	if ( empty( $registered[ $option ] ) ) {
+		return $default;
+	}
+
+	return $registered[ $option ]['default'];
+}
