Commit 61f1b46e authored by Joachim Breitner's avatar Joachim Breitner

Make language extensions their own category in the documentation

I.e. instead of

    .. ghc-flag:: -XUnboxedTuples
        :shortdesc: Enable the use of unboxed tuple syntax.
        :type: dynamic
        :reverse: -XNoUnboxedTuples
        :category:

one simply writes

    .. extension:: UnboxedTuples
        :shortdesc: Enable the use of unboxed tuple syntax.

This allows language extensions to be referenced as

    If :extension:`UnboxedTuples` is enabled, then...

This directive still creates the entries for the `-XUnboxedTuples` flag,
so in particular,

    Set :ghc-flag:`-XUnboxedTuples` if you have to.

still works, and lists of flags in general (e.g. for the manpage)
include these.

I also removed lots of links from the shortdesc of the extensions, when
this link simply points to the section where the extension is defined.

I removed the list of `-X` flags from the flag reference table, but added a
table of extension under “10.1. Language options”

Lots of text in the manual now refers to “extension `Foo`” rather than
“flag `-XFoo`”.

I consider `-XFoo` a historic artifact that stems from when language
extensions were really just flags. These days, the use of `-XFoo` is
(IMHO) deprecated: You should be using `LANGUAGE Foo`, or maybe the
appropriate field in a `.cabal` file. See 9278994a which did this change
to error messages already.

Differential Revision: https://phabricator.haskell.org/D4112
parent b1ad0bb3
......@@ -7,11 +7,8 @@ Foreign function interface (FFI)
single: Foreign function interface
single: interfacing with native code
.. ghc-flag:: -XForeignFunctionInterface
:shortdesc: Enable :ref:`foreign function interface <ffi>`.
:type: dynamic
:reverse: -XNoForeignFunctionInterface
:category: language
.. extension:: ForeignFunctionInterface
:shortdesc: Enable foreign function interface.
:since: 6.8.1
......@@ -22,7 +19,7 @@ definition is part of the Haskell Report on
`http://www.haskell.org/ <http://www.haskell.org/>`__.
FFI support is enabled by default, but can be enabled or disabled
explicitly with the :ghc-flag:`-XForeignFunctionInterface` flag.
explicitly with the :extension:`ForeignFunctionInterface` flag.
GHC implements a number of GHC-specific extensions to the FFI Chapter of the
Haskell 2010 Report. These extensions are described in :ref:`ffi-ghcexts`, but
......@@ -124,11 +121,8 @@ come with GHC. For more details see the
Interruptible foreign calls
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. ghc-flag:: -XInterruptibleFFI
.. extension:: InterruptibleFFI
:shortdesc: Enable interruptible FFI.
:type: dynamic
:reverse: -XNoInterruptibleFFI
:category: language
:since: 7.2.1
......@@ -181,11 +175,8 @@ it is not typically necessary to handle ``ERROR_OPERATION_ABORTED``.
The CAPI calling convention
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. ghc-flag:: -XCApiFFI
:shortdesc: Enable :ref:`the CAPI calling convention <ffi-capi>`.
:type: dynamic
:reverse: -XNoCAPIFFI
:category: language
.. extension:: CApiFFI
:shortdesc: Enable the CAPI calling convention.
:since: 7.10.1
......
......@@ -20,9 +20,29 @@
# :type: table/list/summary (REQUIRED)
# :category: Limit the output to a single category
#
# It also provides a directive to list language extensions:
#
# .. extension::
# :shortdesc: A short description (REQUIRED)
# :noindex: Do not list the extension anywhere (good for duplicates)
#
# This has the side-effect of an appropriate ghc-flag directive for the `-X`
# flag.
#
# Extensions can be referenced with
#
# :extension:`extension`
#
# Language exensions can be listed:
#
# .. extension-print::
# :type: table/list/summary (REQUIRED)
#
# The two main functions in this extension are Flag.after_content() which adds
# flag metadata into the environment, and flagprint.generate_output(), which
# reads the metadata back out and formats it as desired.
#
#
from docutils import nodes
from docutils.parsers.rst import Directive, directives
......@@ -79,10 +99,54 @@ file_defaults = {
### Flag declaration
# Functionality to add a flag to the tables, used by both Flag and LanguageExtension
class GenericFlag(GenericObject):
def register_flag(self, names, category, name_string, shortdesc, flag_type, reverse_string):
# Create nodes for each cell of the table
name_node = nodes.paragraph()
shortdesc_node = nodes.paragraph()
type_node = nodes.paragraph()
reverse_node = nodes.paragraph()
# Nodes expect an internal ViewList type for the content,
# we are just spoofing it here
from docutils.statemachine import ViewList
name_vl = ViewList(initlist=[name_string],
source=self.env.docname, parent=[])
shortdesc_vl = ViewList(initlist=[shortdesc],
source=self.env.docname, parent=[])
type_vl = ViewList(initlist=[flag_type],
source=self.env.docname, parent=[])
reverse_vl = ViewList(initlist=[reverse_string],
source=self.env.docname, parent=[])
# Parse the content into the nodes
self.state.nested_parse(name_vl, 0, name_node)
self.state.nested_parse(shortdesc_vl, 0, shortdesc_node)
self.state.nested_parse(type_vl, 0, type_node)
self.state.nested_parse(reverse_vl, 0, reverse_node)
# The parsing adds extra layers that we don't need
name_node = name_node[0]
shortdesc_node = shortdesc_node[0]
# Append this flag to the environment, initializing if necessary
if not hasattr(self.env, 'all_flags'):
self.env.all_flags = []
self.env.all_flags.append({
'names': names,
'docname': self.env.docname,
'category': category,
'cells': [name_node, shortdesc_node, type_node, reverse_node],
})
# This class inherits from Sphinx's internal GenericObject, which drives
# the add_object_type() utility function. We want to keep that tooling,
# but need to override some of the functionality.
class Flag(GenericObject):
class Flag(GenericFlag):
# The options that can be passed to our directive and their validators
option_spec = {
......@@ -146,48 +210,119 @@ class Flag(GenericObject):
if 'reverse' in self.options and self.options['reverse'] != '':
reverse_string = ':ghc-flag:`' + self.options['reverse'] + '`'
# Create nodes for each cell of the table
name_node = nodes.paragraph()
shortdesc_node = nodes.paragraph()
type_node = nodes.paragraph()
reverse_node = nodes.paragraph()
self.register_flag(
self.names,
self.category,
name_string,
self.options['shortdesc'],
self.options['type'],
reverse_string)
# This class inherits from Sphinx's internal GenericObject, which drives
# the add_object_type() utility function. We want to keep that tooling,
# but need to override some of the functionality.
class LanguageExtension(GenericFlag):
# The options that can be passed to our directive and their validators
option_spec = {
'shortdesc': directives.unchanged_required,
'noindex': directives.flag
}
# The index directive generated unless :noindex: is specified
indextemplate = 'pair: %s; Language Extension'
# Invert the flag
@staticmethod
def _noname(name):
if name[:2] == "No":
return name[2:]
else:
return "No%s" % name
@staticmethod
def _onname(name):
if name[:2] == "No":
return name[2:]
else:
return name
# Add additional targets
def add_target_and_index(self, name, sig, signode):
GenericFlag.add_target_and_index(self, name, sig, signode)
# Mostly for consistency in URL anchors
signode['ids'].append('ghc-flag--X%s' % name)
# So that anchors stay valid even if an extension turns to on-by-default
signode['ids'].append('extension-%s' % self._noname(name))
targetname = '%s-%s' % (self.objtype, name)
# Add index entries for the -XFoo flag
self.indexnode['entries'].append(('pair', '-X%s; GHC option' % name,
targetname, '', None))
# Make this also addressable using :ghc-flag:-XFoo
self.env.domaindata['std']['objects']['ghc-flag', '-X%s' % name] = \
self.env.docname, 'extension-%s' % name
# Make this also addressable using :extension:-XNoFoo
self.env.domaindata['std']['objects']['extension', self._noname(name)] = \
self.env.docname, 'extension-%s' % name
# Override the (empty) function that is called at the end of run()
# to append metadata about this flag into the environment
def after_content(self):
# If noindex, then do not include this extension in the table
if 'noindex' in self.options:
return
# Validity checking
if len(self.names) < 1:
raise SphinxError('extension needs at least one name')
primary_name = self.names[0]
if 'shortdesc' not in self.options:
raise SphinxError('extension (%s) directive missing :shortdesc: key' % primary_name)
# Register the corresponding flags
for name in self.names:
self.register_flag(
['-X%s' % name],
'language',
':extension:`-X%s <%s>`' % (name, primary_name),
self.options['shortdesc'],
'dynamic',
':extension:`-X%s <%s>`' % (self._noname(name), primary_name))
# Register the extension for the table, under the "on name" (no No...)
onname = self._onname(primary_name)
name_node = nodes.paragraph()
shortdesc_node = nodes.paragraph()
# Nodes expect an internal ViewList type for the content,
# we are just spoofing it here
from docutils.statemachine import ViewList
name_vl = ViewList(initlist=[name_string],
name_vl = ViewList(initlist=[':extension:`%s`' % onname],
source=self.env.docname, parent=[])
shortdesc_vl = ViewList(initlist=[self.options['shortdesc']],
source=self.env.docname, parent=[])
type_vl = ViewList(initlist=[self.options['type']],
source=self.env.docname, parent=[])
reverse_vl = ViewList(initlist=[reverse_string],
source=self.env.docname, parent=[])
# Parse the content into the nodes
self.state.nested_parse(name_vl, 0, name_node)
self.state.nested_parse(shortdesc_vl, 0, shortdesc_node)
self.state.nested_parse(type_vl, 0, type_node)
self.state.nested_parse(reverse_vl, 0, reverse_node)
# The parsing adds extra layers that we don't need
name_node = name_node[0]
shortdesc_node = shortdesc_node[0]
# Append this flag to the environment, initializing if necessary
if not hasattr(self.env, 'all_flags'):
self.env.all_flags = []
self.env.all_flags.append({
'names': self.names,
if not hasattr(self.env, 'all_extensions'):
self.env.all_extensions = []
self.env.all_extensions.append({
'name': onname,
'docname': self.env.docname,
'category': self.category,
'cells': [name_node, shortdesc_node, type_node, reverse_node],
'cells': [name_node, shortdesc_node]
})
### Flag Printing
# Taken from Docutils source inside the ListTable class. We must bypass
......@@ -285,7 +420,7 @@ def generate_flag_summary(flags, category):
return summary_node
# Output dispatch table
handlers = {
flag_handlers = {
'table': generate_flag_table,
'list': generate_flag_list,
'summary': generate_flag_summary
......@@ -303,7 +438,7 @@ class flagprint(nodes.General, nodes.Element):
if category not in categories:
error = "flagprint: Unknown category: " + category
raise ValueError(error)
if output_type not in handlers:
if output_type not in flag_handlers:
error = "flagprint: Unknown output type: " + output_type
raise ValueError(error)
......@@ -326,7 +461,7 @@ class flagprint(nodes.General, nodes.Element):
def generate_output(self, app, fromdocname):
env = app.builder.env
# Filter flags before passing to handlers
# Filter flags before passing to flag_handlers
flags = []
for flag_info in sorted(env.all_flags,
......@@ -345,10 +480,9 @@ class flagprint(nodes.General, nodes.Element):
flags.append(flag_info)
handler = handlers[self.options['type']]
handler = flag_handlers[self.options['type']]
self.replace_self(handler(flags, self.options['category']))
# A directive to create flagprint nodes
class FlagPrintDirective(Directive):
......@@ -368,6 +502,97 @@ class FlagPrintDirective(Directive):
node = flagprint(output_type=self.options['type'], category=category)
return [node]
### Extension Printing
# Generate a table of flags
def generate_extension_table(extensions):
# Create column headers for table
header = []
for h in ["Extension", "Description"]:
inline = nodes.inline(text=h)
header.append(inline)
extension_list = [header]
for extension_info in extensions:
extension_list.append(extension_info['cells'])
# The column width hints only apply to html,
# latex widths are set in file (see flags.rst)
table = build_table_from_list(extension_list, [28, 72])
# Flag tables have lots of content, so we need to set 'longtable'
# to allow for pagebreaks. (latex specific)
table['classes'].append('longtable')
return table
# Output dispatch table
extension_handlers = {
'table': generate_extension_table,
}
# Generic node for printing extension output
class extensionprint(nodes.General, nodes.Element):
def __init__(self, output_type='', **kwargs):
nodes.Element.__init__(self, rawsource='', **kwargs)
# Verify options
if output_type not in extension_handlers:
error = "extensionprint: Unknown output type: " + output_type
raise ValueError(error)
# Store the options
self.options = {
'type': output_type,
}
# The man writer has a copy issue, so we explicitly override it here
def copy(self):
newnode = extensionprint(output_type=self.options['type'], **self.attributes)
newnode.source = self.source
newnode.line = self.line
return newnode
def generate_output(self, app, fromdocname):
env = app.builder.env
extensions = []
for extension_info in sorted(env.all_extensions,
key=lambda fi: fi['name'].lower()):
# Resolve all references as if they were originated from this node.
# This fixes the relative uri.
for cell in extension_info['cells']:
for ref in cell.traverse(addnodes.pending_xref):
ref['refdoc'] = fromdocname
env.resolve_references(cell, extension_info['docname'], app.builder)
extensions.append(extension_info)
handler = extension_handlers[self.options['type']]
self.replace_self(handler(extensions))
# A directive to create extensionprint nodes
class ExtensionPrintDirective(Directive):
option_spec = {
'type': directives.unchanged_required
}
def run(self):
# Create a extensionprint node
node = extensionprint(output_type=self.options['type'])
return [node]
### Additional processing
......@@ -377,16 +602,20 @@ def process_print_nodes(app, doctree, fromdocname):
for node in doctree.traverse(flagprint):
node.generate_output(app, fromdocname)
for node in doctree.traverse(extensionprint):
node.generate_output(app, fromdocname)
# To avoid creating duplicates in the serialized environment, clear all
# flags originating from a file before re-reading it.
def purge_flags(app, env, docname):
if not hasattr(env, 'all_flags'):
return
env.all_flags = [flag for flag in env.all_flags
if flag['docname'] != docname]
if hasattr(env, 'all_flags'):
env.all_flags = [flag for flag in env.all_flags
if flag['docname'] != docname]
if hasattr(env, 'all_extensions'):
env.all_extensions = [ext for ext in env.all_extensions
if ext['docname'] != docname]
### Initialization
......@@ -396,10 +625,20 @@ def setup(app):
app.add_object_type('ghc-flag', 'ghc-flag')
app.add_directive_to_domain('std', 'ghc-flag', Flag)
# Add extension directive, and override the class with our own
app.add_object_type('extension', 'extension')
app.add_directive_to_domain('std', 'extension', LanguageExtension)
# NB: language-extension would be misinterpreted by sphinx, and produce
# lang="extensions" XML attributes
# Add new node and directive
app.add_node(flagprint)
app.add_directive('flag-print', FlagPrintDirective)
# Add new node and directive
app.add_node(extensionprint)
app.add_directive('extension-print', ExtensionPrintDirective)
# Add our generator and cleanup functions as callbacks
app.connect('doctree-resolved', process_print_nodes)
app.connect('env-purge-doc', purge_flags)
......
......@@ -175,8 +175,6 @@ More details in :ref:`packages`
:category: packages
.. flags-language-options::
Language options
----------------
......@@ -184,15 +182,6 @@ Language options can be enabled either by a command-line option
``-Xblah``, or by a ``{-# LANGUAGE blah #-}`` pragma in the file itself.
See :ref:`options-language`.
.. tabularcolumns::
| p{\dimexpr 0.36\textwidth-2\tabcolsep} |
p{\dimexpr 0.25\textwidth-2\tabcolsep} |
p{\dimexpr 0.11\textwidth-2\tabcolsep} |
p{\dimexpr 0.29\textwidth-2\tabcolsep} |
.. flag-print::
:type: table
:category: language
Warnings
--------
......
......@@ -1023,13 +1023,8 @@ Type defaulting in GHCi
single: Type defaulting; in GHCi
single: Show class
.. ghc-flag:: -XExtendedDefaultRules
:shortdesc: Use GHCi's
:ref:`extended default rules <extended-default-rules>` in a normal
module.
:type: dynamic
:reverse: -XNoExtendedDefaultRules
:category: language
.. extension:: ExtendedDefaultRules
:shortdesc: Use GHCi's extended default rules in a normal module.
:since: 6.8.1
......@@ -1065,7 +1060,7 @@ and defaults the type variable if
3. At least one of the classes ``Ci`` is numeric.
At the GHCi prompt, or with GHC if the :ghc-flag:`-XExtendedDefaultRules` flag
At the GHCi prompt, or with GHC if the :extension:`ExtendedDefaultRules` flag
is given, the types are instead resolved with the following method:
Find all the unsolved constraints. Then:
......@@ -1118,7 +1113,7 @@ Interactive classes
.. index::
single: Interactive classes
The interactive classes (only relevant when :ghc-flag:`-XExtendedDefaultRules`
The interactive classes (only relevant when :extension:`ExtendedDefaultRules`
is in effect) are: any numeric class, ``Show``, ``Eq``, ``Ord``,
``Foldable`` or ``Traversable``.
......@@ -1132,7 +1127,7 @@ Extended rules around ``default`` declarations
single: default declarations
Since the rules for defaulting are relaxed under
:ghc-flag:`-XExtendedDefaultRules`, the rules for ``default`` declarations
:extension:`ExtendedDefaultRules`, the rules for ``default`` declarations
are also relaxed. According to Section 4.3.4 of the Haskell 2010 Report,
a ``default`` declaration looks like ``default (t1, ..., tn)`` where, for
each ``ti``, ``Num ti`` must hold. This is relaxed to say that for each
......@@ -2781,7 +2776,7 @@ commonly used commands.
Infers and prints the type of ⟨expression⟩, but without fiddling
with type variables or class constraints. This is useful when you
are using :ghc-flag:`-XTypeApplications` and care about the distinction
are using :extension:`TypeApplications` and care about the distinction
between specified type variables (available for type application)
and inferred type variables (not available). This mode sometimes prints
constraints (such as ``Show Int``) that could readily be solved, but
......@@ -2799,7 +2794,7 @@ commonly used commands.
if possible. In this mode, if the inferred type is constrained by
any interactive class (``Num``, ``Show``, ``Eq``, ``Ord``, ``Foldable``,
or ``Traversable``), the constrained type variable(s) are defaulted
according to the rules described under :ghc-flag:`-XExtendedDefaultRules`.
according to the rules described under :extension:`ExtendedDefaultRules`.
This mode is quite useful when the inferred type is quite general (such
as for ``foldr``) and it may be helpful to see a more concrete
instantiation.
......@@ -2994,7 +2989,7 @@ that option apply to loaded modules too. For example
:seti -XMonoLocalBinds
It would be undesirable if :ghc-flag:`-XMonoLocalBinds` were to apply to loaded
It would be undesirable if :extension:`MonoLocalBinds` were to apply to loaded
modules too: that might cause a compilation error, but more commonly it
will cause extra recompilation, because GHC will think that it needs to
recompile the module because the flags have changed.
......
This diff is collapsed.
......@@ -221,11 +221,8 @@ to GHC's runtime system you can enclose them in ``+RTS ... -RTS`` (see
Options affecting the C pre-processor
-------------------------------------
.. ghc-flag:: -XCPP
:shortdesc: Enable the :ref:`C preprocessor <c-pre-processor>`.
:type: dynamic
:reverse: -XNoCPP
:category: language
.. extension:: CPP
:shortdesc: Enable the C preprocessor.
:since: 6.8.1
......
......@@ -158,40 +158,40 @@ reasons this security mechanism would fail without Safe Haskell:
it.
Safe Haskell prevents all these attacks. This is done by compiling the
RIO module with the :ghc-flag:`-XSafe` or :ghc-flag:`-XTrustworthy` flag and compiling
``Danger`` with the :ghc-flag:`-XSafe` flag. We explain each below.
RIO module with the :extension:`Safe` or :extension:`Trustworthy` flag and compiling
``Danger`` with the :extension:`Safe` flag. We explain each below.
The use of :ghc-flag:`-XSafe` to compile ``Danger`` restricts the features of
The use of :extension:`Safe` to compile ``Danger`` restricts the features of
Haskell that can be used to a `safe subset <#safe-language>`__. This
includes disallowing ``unsafePerformIO``, Template Haskell, pure FFI
functions, RULES and restricting the operation of Overlapping Instances.
The :ghc-flag:`-XSafe` flag also restricts the modules can be imported by
The :extension:`Safe` flag also restricts the modules can be imported by
``Danger`` to only those that are considered trusted. Trusted modules
are those compiled with :ghc-flag:`-XSafe`, where GHC provides a mechanical
are those compiled with :extension:`Safe`, where GHC provides a mechanical
guarantee that the code is safe. Or those modules compiled with
:ghc-flag:`-XTrustworthy`, where the module author claims that the module is
:extension:`Trustworthy`, where the module author claims that the module is
Safe.
This is why the RIO module is compiled with :ghc-flag:`-XSafe` or
:ghc-flag:`-XTrustworthy`>, to allow the ``Danger`` module to import it. The
:ghc-flag:`-XTrustworthy` flag doesn't place any restrictions on the module like
:ghc-flag:`-XSafe` does (expect to restrict overlapping instances to `safe
This is why the RIO module is compiled with :extension:`Safe` or
:extension:`Trustworthy`>, to allow the ``Danger`` module to import it. The
:extension:`Trustworthy` flag doesn't place any restrictions on the module like
:extension:`Safe` does (expect to restrict overlapping instances to `safe
overlapping instances <#safe-overlapping-instances>`__). Instead the
module author claims that while code may use unsafe features internally,
it only exposes an API that can used in a safe manner.
However, the unrestricted use of :ghc-flag:`-XTrustworthy` is a problem as an
However, the unrestricted use of :extension:`Trustworthy` is a problem as an
arbitrary module can use it to mark themselves as trusted, yet
:ghc-flag:`-XTrustworthy` doesn't offer any guarantees about the module, unlike
:ghc-flag:`-XSafe`. To control the use of trustworthy modules it is recommended
:extension:`Trustworthy` doesn't offer any guarantees about the module, unlike
:extension:`Safe`. To control the use of trustworthy modules it is recommended
to use the :ghc-flag:`-fpackage-trust` flag. This flag adds an extra requirement
to the trust check for trustworthy modules. It requires that for a
trustworthy modules to be considered trusted, and allowed to be used in
:ghc-flag:`-XSafe` compiled code, the client C compiling the code must tell GHC
:extension:`Safe` compiled code, the client C compiling the code must tell GHC
that they trust the package the trustworthy module resides in. This is
essentially a way of for C to say, while this package contains
trustworthy modules that can be used by untrusted modules compiled with
:ghc-flag:`-XSafe`, I trust the author(s) of this package and trust the modules
:extension:`Safe`, I trust the author(s) of this package and trust the modules
only expose a safe API. The trust of a package can be changed at any
time, so if a vulnerability found in a package, C can declare that
package untrusted so that any future compilation against that package
......@@ -199,7 +199,7 @@ would fail. For a more