Forráskód Böngészése

Carbon/C++ Interop: Importing C/C++ object-like macros (#6676)

A proposal for importing C/C++ object-like macros into Carbon.

Based on the design doc: [Carbon: C++ interop for C/C++ object-like
macros](https://docs.google.com/document/d/1CCB05gi3uHfDAXUy6DvOHxsn0spXcSrL_2Ye9QOSwrs/edit?tab=t.0).

Part of https://github.com/carbon-language/carbon-lang/issues/6303
Ivana Ivanovska 2 hónapja
szülő
commit
d9570b4d37
1 módosított fájl, 342 hozzáadás és 0 törlés
  1. 342 0
      proposals/p6676.md

+ 342 - 0
proposals/p6676.md

@@ -0,0 +1,342 @@
+# Carbon/C++ Interop: Importing C/C++ object-like macros
+
+<!--
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+Exceptions. See /LICENSE for license information.
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+-->
+
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/6676)
+
+<!-- toc -->
+
+## Table of contents
+
+-   [Abstract](#abstract)
+-   [Problem](#problem)
+-   [Background](#background)
+    -   [Macros in C++](#macros-in-c)
+        -   [Object-like macros](#object-like-macros)
+        -   [Function-like macros](#function-like-macros)
+        -   [Predefined macros](#predefined-macros)
+    -   [Swift / C interop [GitHub][documentation]](#swift--c-interop-githubdocumentation)
+-   [Proposal](#proposal)
+-   [Details](#details)
+    -   [Namespace](#namespace)
+    -   [Constant type](#constant-type)
+    -   [Constant value](#constant-value)
+    -   [Constant expressions](#constant-expressions)
+    -   [Empty macros](#empty-macros)
+    -   [Implementation](#implementation)
+-   [Rationale](#rationale)
+-   [Alternatives considered](#alternatives-considered)
+-   [Open questions](#open-questions)
+    -   [Object-like macros](#object-like-macros-1)
+    -   [Function-like macros](#function-like-macros-1)
+    -   [Predefined macros](#predefined-macros-1)
+
+<!-- tocstop -->
+
+## Abstract
+
+This proposal addresses importing object-like C/C++ macros into Carbon.
+
+## Problem
+
+C/C++ object-like macros are used to define constants, conditional compilation,
+header guards, etc. and are common for low-level, cross-platform libraries, with
+C like APIs. Despite the recommendations for replacing them in C++ with safer
+techniques (for example `constexpr`), they remain to be present in C++ code
+bases. Of particular interest for the interoperability are cases such as macros
+present in the APIs of the standard C++ library (for example error codes in
+`<errno.h>`), which can't be easily changed, but remain to be widely used in
+low-level C++ libraries. A mechanism for properly importing and handling these
+macros is necessary for a seamless interoperability.
+
+## Background
+
+### Macros in C++
+
+Macros are defined with the `#define` directive and processed by the
+preprocessor, which replaces the occurrences of the defined identifier with a
+replacement-list. There are two types of macros: _object-like_ and
+_function-like_ macros. The directive `#undef` undefines a defined macro.
+
+#### Object-like macros
+
+```
+#define identifier replacement-list (optional)
+```
+
+Replacement-lists can have elements that are:
+
+-   Literals: integer, floating-point, string, char, bool etc.
+-   Non-literal types: structs, classes etc.
+-   Identifiers: pointing to other macros, variables, types, keywords etc.
+-   Operators: `+, -, <<, >>, |` etc. with one or more operands.
+
+#### Function-like macros
+
+```
+#define identifier(parameters) replacement-list (optional)
+#define identifier(parameters, ...) replacement-list (optional)
+#define identifier(...) replacement-list (optional)
+```
+
+The syntax of function-like macros is similar to the syntax of a function call.
+They accept a list of arguments and replace their occurrence in the
+replacement-list.
+
+As this is only a text replacement, it can lead to unexpected behaviour if the
+arguments are not properly separated with brackets.
+
+In function-like macros, the operators `#` and `##` enable:
+
+-   `#operator (stringification)` - the arguments of the macro are converted to
+    a string literal without expanding the argument. When `#` is used in front
+    of a parameter in a macro definition (`#param`), the preprocessor replaces
+    `#param` with the argument tokens as a string literal.
+
+-   `##operator (concatenation or token pasting)` - two tokens are merged in a
+    single token during a macro expansion. For example, when `a##b` is used as
+    part of the macro definition, the preprocessor merges `a` and `b`, removing
+    any white spaces in between to form a single token. When some of the tokens
+    on either side of the `##` operator are parameter names, they are replaced
+    by the actual argument before the execution of `##`. This is used for
+    example to create new identifiers like variables, function names etc. and in
+    general to avoid creating repeated boilerplate code.
+
+#### Predefined macros
+
+There are also predefined macros available in every translation unit. Examples
+include: `__cplusplus`, `__FILE__`, `__LINE__`, `__DATE__`, `__TIME__` etc.
+
+### Swift / C interop [[GitHub](https://github.com/swiftlang/swift/blob/main/lib/ClangImporter/ImportMacro.cpp)][[documentation](https://developer.apple.com/documentation/swift/using-imported-c-macros-in-swift)]
+
+Swift supports importing object-like C macros as global constants. Macros that
+use integer, floating-point and string literals are supported. Also simple
+operators like `+, -, <<, >>` etc. between literals or macros are supported.
+
+Function-like macros are not supported. Instead, using Swift functions and
+generics is recommended.
+
+## Proposal
+
+An object-like macro that evaluates to a constant expression is imported from
+C++ as a constant in Carbon. For example:
+
+C++:
+
+```
+#define BUFFER_SIZE 4096
+```
+
+Carbon:
+
+```
+let a: i32 = Cpp.BUFFER_SIZE; // Cpp.BUFFER_SIZE is imported as an int value of type i32 with a value of 4096
+
+```
+
+## Details
+
+### Namespace
+
+The macro is evaluated in the global Cpp namespace and accessible as
+Cpp.BUFFER_SIZE.
+
+### Constant type
+
+The type of the imported constant is deduced by Clang by evaluating the constant
+expression and then mapped to a Carbon type following the existing
+[Carbon <-> C++ type mapping rules](https://github.com/carbon-language/carbon-lang/blob/trunk/proposals/p5448.md).
+
+### Constant value
+
+The value is deduced by evaluating the tokens of a replacement list as a
+constant expression.
+
+### Constant expressions
+
+The replacement list in the object-like macro expanding to a constant expression
+can contain:
+
+-   **Operators**: arithmetic: `+, -, *, /`; bitwise: `|, &, ^, <<, >>` ;
+    logical: `||, &&`; comparison: `<, >, <=, >=, ==`; casts etc, with arbitrary
+    number of operands.
+
+For example:
+
+```
+#define ADDITION 1+2+3
+```
+
+-   **Chained macros**: macros that expand to another macro which evaluates to a
+    constant.
+
+For example:
+
+```
+#define VALUE 123
+#define MY_VALUE VALUE
+```
+
+-   **Enum constants and `constexpr` variables**: if a macro's replacement list
+    refers to a named constant, such as an enum constant or a `constexpr`
+    variable, it is imported as an alias rather than as a literal value. This
+    allows Carbon to preserve the specific type of the constant (such as `Color`
+    in the example below). In the case of `constexpr` variables, importing as an
+    alias also preserves addressability (that the constant is an lvalue), which
+    would be lost if only the value were imported.
+
+For example:
+
+C++:
+
+```
+enum class Color { Red = 1, Green = 2 };
+#define GREEN_COLOR Color::Green
+
+constexpr int kValue = 123;
+#define VALUE kValue
+```
+
+Carbon:
+
+```
+// Cpp.GREEN_COLOR is an alias to Cpp.Color.Green which has a type Cpp.Color.
+let b: Cpp.Color = Cpp.GREEN_COLOR;
+
+// Cpp.VALUE is an alias to kValue.
+let a: i32 = Cpp.VALUE;
+```
+
+The macro will be evaluated by default in the global namespace (for example
+`Cpp.VALUE`). Evaluating it in a child namespace (`Cpp.SomeNamespace.VALUE`) may
+also be possible, though details about that are outside of the scope of this
+proposal.
+
+### Empty macros
+
+The macro should have at least one value in the replacement-list so that it’s
+imported. For example, the following macro won’t have a Carbon equivalent:
+
+```
+#define EMPTY
+```
+
+### Implementation
+
+1. _Name lookup_: When a C++ macro name is encountered in Carbon it is looked-up
+   before any other name. Following the C++ rules, this allows the macro to be
+   found in case there is a non-macro with the same name (for example named
+   variable).
+
+2. _Macro import_: If a macro is found, it is imported as a constant to Carbon,
+   by parsing the tokens of the replacement list to a constant expression and
+   evaluating the result.
+
+For example, given a macro:
+
+```
+#define MY_MACRO <some tokens>
+```
+
+The following constexpr will be (effectively) generated and imported:
+
+```
+constexpr inline decltype(auto) __carbon_import_MY_MACRO = (MY_MACRO);
+```
+
+That is, we would try to generate such a `constexpr` and import the macro if
+that succeeds.
+
+## Rationale
+
+This work contributes to the Carbon’s goal for seamless interoperability with
+C++
+([Interoperability with and migration from existing C++ code](https://github.com/carbon-language/carbon-lang/blob/trunk/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code)),
+by enabling reusing and migrating existing macros, while adhering to the best
+practices to use constant variables.
+
+## Alternatives considered
+
+-   Implementation
+
+    a) Reuse Swift/C implementation
+
+    -   _Reason to do this_: use existing implementation.
+    -   _Reason not to do this_: this implementation is much more limited,
+        longer and harder to maintain than the proposed one, as it manually
+        scans the replacement tokens and tries to evaluate them, instead of
+        using Clang for that. Limitations include for example type of supported
+        operators, number and type of operands etc. Also, due to the licensing,
+        it is not directly available.
+
+    b) Manually scanning tokens (own implementation)
+
+    -   _Reason not to do this_: this is a much longer implementation compared
+        to the proposed one, with much more limitations compared to using Clang
+        to do that.
+
+-   Importing macros that refer to enum constants and `constexpr` variables as
+    constant values, instead of aliases in Carbon
+    -   _Reason to do this_: This would be a simpler implementation and
+        consistent with how other object-like macros are imported.
+    -   _Reason not to do this_: This would not preserve the enum's type (for
+        example, `Color::Green` would be imported as an integer literal `2`
+        instead of as a constant of type `Color`) or the addressability of
+        `constexpr` variables (which are lvalues).
+
+## Open questions
+
+### Object-like macros
+
+The support for the following cases still needs to be clarified:
+
+-   non-constant variable name
+
+```
+int x;
+#define VAL x
+```
+
+-   types:
+
+```
+#define SHORT_TYPE short
+```
+
+-   Statements or keywords:
+
+```
+#define RET return
+#define FOREVER for(;;)
+```
+
+-   Expanding macros in child namespaces (`Cpp.SubNamespace.MACRO`)
+
+### Function-like macros
+
+The support for function-like macros will still need to be discussed in detail.
+The current proposal could be extended for function-like macros in the following
+direction:
+
+Given a function-like macro:
+
+```
+#define MY_MACRO(a, b) ((a) + (b))
+```
+
+A function template whose body invokes the macro could be (eventually) generated
+and imported:
+
+```
+constexpr decltype(auto) __carbon_import_MY_MACRO(auto a, auto b) {
+  return (MY_MACRO(a, b));
+}
+```
+
+### Predefined macros
+
+Details on the support for predefined macros remains open as well.