mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:20:10 +01:00
Compare commits
162
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
733b76efea | ||
|
|
69d745c6d7 | ||
|
|
d6a741f208 | ||
|
|
1e3906177c | ||
|
|
5e2f693db1 | ||
|
|
1cc699ddda | ||
|
|
ad0a4ea8a4 | ||
|
|
f91990aa87 | ||
|
|
e7626f46cc | ||
|
|
af04d08965 | ||
|
|
a6061d975c | ||
|
|
896338d281 | ||
|
|
d8fe95cccb | ||
|
|
df33276f6b | ||
|
|
327cb2396a | ||
|
|
1d5113649b | ||
|
|
cdfa57f230 | ||
|
|
46f46a538d | ||
|
|
114cf401c2 | ||
|
|
7c94878c10 | ||
|
|
82fad29028 | ||
|
|
26ddada3ae | ||
|
|
bab35c114e | ||
|
|
96529e16bd | ||
|
|
43867a678b | ||
|
|
5b1de7633c | ||
|
|
f31e1685fd | ||
|
|
3cdb159067 | ||
|
|
12d7574636 | ||
|
|
cc5a42691e | ||
|
|
19efec93be | ||
|
|
3db691ecef | ||
|
|
6cc08ae6e6 | ||
|
|
ab977ee04d | ||
|
|
be0c07dc7e | ||
|
|
b79d9adeca | ||
|
|
b74e0d1260 | ||
|
|
9f1a0c816c | ||
|
|
6f02354d62 | ||
|
|
0635f4628f | ||
|
|
d1dc8e820d | ||
|
|
e1f30669af | ||
|
|
f483a28f2f | ||
|
|
cc4fd39238 | ||
|
|
013a417ea5 | ||
|
|
8fd4156616 | ||
|
|
05ba1d7356 | ||
|
|
8e0d856725 | ||
|
|
ea409f7cbf | ||
|
|
fed9e8c878 | ||
|
|
451b50a3ad | ||
|
|
912825feaf | ||
|
|
81ed4d829d | ||
|
|
9266ced4e3 | ||
|
|
1fa7a64cd4 | ||
|
|
562b423830 | ||
|
|
6f0ec37a8b | ||
|
|
0851d657c8 | ||
|
|
07afa07127 | ||
|
|
f7a767a77b | ||
|
|
be283a0744 | ||
|
|
cd6ab7ce8e | ||
|
|
0075d530b9 | ||
|
|
5b7c908e8b | ||
|
|
2bbcfa5e1a | ||
|
|
dfac728571 | ||
|
|
8f3b057179 | ||
|
|
17180558e5 | ||
|
|
39eac6f277 | ||
|
|
2787089247 | ||
|
|
3578dd6b91 | ||
|
|
8b59e85b16 | ||
|
|
47e9d62fd5 | ||
|
|
f4260feee4 | ||
|
|
aec2534e9d | ||
|
|
2e0d9dc709 | ||
|
|
396756c151 | ||
|
|
bf6a14ac39 | ||
|
|
9ed045ec25 | ||
|
|
2318294eb5 | ||
|
|
d9b901394b | ||
|
|
181a592b8c | ||
|
|
1ef35e8299 | ||
|
|
aa8e96ac72 | ||
|
|
5503f643c6 | ||
|
|
899e54de36 | ||
|
|
a2ba7f1262 | ||
|
|
2b1fe7c292 | ||
|
|
a0416a1250 | ||
|
|
c68c4007ab | ||
|
|
1f7d8e4675 | ||
|
|
557039648b | ||
|
|
786e02cb3e | ||
|
|
262e24a2a0 | ||
|
|
18f87e4f79 | ||
|
|
85da6cae01 | ||
|
|
d6be20641c | ||
|
|
4a0c1ddd8e | ||
|
|
5ae3629e49 | ||
|
|
25b85a55ae | ||
|
|
15dabbfb0d | ||
|
|
25793358c3 | ||
|
|
37b238fa28 | ||
|
|
311670c84a | ||
|
|
965879a9a9 | ||
|
|
415cd6f8f0 | ||
|
|
7345f4e860 | ||
|
|
29a8b315d3 | ||
|
|
e0305684b0 | ||
|
|
2af5f971da | ||
|
|
f9e1806c4a | ||
|
|
e0c6800ab3 | ||
|
|
a345a74145 | ||
|
|
86d05b598b | ||
|
|
7b6e3dfbb0 | ||
|
|
17657d0586 | ||
|
|
3ef991466d | ||
|
|
44f2a68ee0 | ||
|
|
81215e873e | ||
|
|
bc38deb16c | ||
|
|
6d2387cb77 | ||
|
|
000b4f3fa5 | ||
|
|
e06eb8f532 | ||
|
|
8e824d02be | ||
|
|
8e5b358ec2 | ||
|
|
ce50f181f1 | ||
|
|
fd2d210c63 | ||
|
|
08148f3a3a | ||
|
|
10beae2c20 | ||
|
|
6d1130f657 | ||
|
|
14b72f16da | ||
|
|
7b3f120f97 | ||
|
|
0482b27c6b | ||
|
|
98e2567524 | ||
|
|
d47b6221ae | ||
|
|
87fc05750b | ||
|
|
2e32f309eb | ||
|
|
1d71e7a707 | ||
|
|
613a139bef | ||
|
|
c5761d2d16 | ||
|
|
943cd41924 | ||
|
|
c006013e0c | ||
|
|
aeed8f608b | ||
|
|
8b907b5a60 | ||
|
|
6f1f59a385 | ||
|
|
27cc14848a | ||
|
|
fbe917b949 | ||
|
|
bad9beddc7 | ||
|
|
6706162582 | ||
|
|
ffe8f8f67d | ||
|
|
5d1973ab93 | ||
|
|
610094ccfd | ||
|
|
4df2b6ea9d | ||
|
|
1e9708e2cc | ||
|
|
4c69a1baf0 | ||
|
|
c1fd771242 | ||
|
|
5d41529590 | ||
|
|
0a4fd2cb7e | ||
|
|
2e5b195813 | ||
|
|
6ac561afe9 | ||
|
|
70c401f85f | ||
|
|
39fd358059 |
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: Bazel usage
|
||||
description:
|
||||
Instructions for using Bazel or Bazelisk to build, test, and debug in the
|
||||
Carbon repository.
|
||||
---
|
||||
|
||||
# Bazel usage
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
This skill documents how best to use Bazel when building, testing, or
|
||||
manipulating the Carbon repository's Bazel in any way.
|
||||
|
||||
## Bazel wrappers
|
||||
|
||||
Carbon uses Bazel for its build system. To ensure consistent versions, the
|
||||
project uses Bazelisk.
|
||||
|
||||
> [!IMPORTANT] Always use `bazelisk` whenever you want to run Bazel. Never run
|
||||
> `bazel` directly in the Carbon project. Anything you want to do with `bazel`
|
||||
> can be done with the `bazelisk` command instead.
|
||||
|
||||
- **Bazelisk**: Try to use `bazelisk` in your existing `$PATH` if available.
|
||||
- **`run_bazelisk.py`**: If `bazelisk` isn't available, use
|
||||
`./scripts/run_bazelisk.py` to run bazelisk without it being installed.
|
||||
|
||||
## Essential commands
|
||||
|
||||
### Building
|
||||
|
||||
- **Build all**: `bazelisk build //...`
|
||||
- **Build toolchain**: `bazelisk build //toolchain/...`
|
||||
- **Build specific target**: `bazelisk build //toolchain:carbon`
|
||||
|
||||
### Testing
|
||||
|
||||
- **Test all**: `bazelisk test //...:all`
|
||||
- **Test toolchain**: `bazelisk test //toolchain/...`
|
||||
- **Test examples**: `bazelisk test //examples/...`
|
||||
|
||||
> [!TIP] Running all of the tests can be slow, so try to narrowly test the
|
||||
> immediately relevant parts of the project first, and only expand coverage as
|
||||
> necessary to be confident in the changes.
|
||||
|
||||
> [!TIP] For specialized instructions on testing and developing the Carbon
|
||||
> toolchain, consult these skills:
|
||||
>
|
||||
> - [Toolchain tests](/.agents/skills/toolchain_tests/SKILL.md): For
|
||||
> authoring, structuring, and running `file_test` tests.
|
||||
> - [Toolchain development](/.agents/skills/toolchain_development/SKILL.md):
|
||||
> For architecture, essential commands, and debugging the toolchain.
|
||||
|
||||
### Running binaries built by Bazel
|
||||
|
||||
> [!IMPORTANT] Always manually run binaries built by Bazel using the
|
||||
> `bazelisk run` command. Never run the binary directly from `bazel-bin/`.
|
||||
|
||||
You can run the Carbon driver or command line directly via Bazel:
|
||||
|
||||
- `bazelisk run //toolchain -- compile --phase=parse toolchain/parse/testdata/basics/empty.carbon`
|
||||
|
||||
## Advanced configurations
|
||||
|
||||
### AddressSanitizer (ASan)
|
||||
|
||||
To enable ASan for local testing:
|
||||
|
||||
- Pass `--config=asan`: `bazelisk test --config=asan //...`
|
||||
|
||||
## Common pitfalls and troubleshooting
|
||||
|
||||
### `bazel clean`
|
||||
|
||||
Changes to packages installed on your system (like changing LLVM versions or
|
||||
installing `libc++`) may not be noticed by Bazel.
|
||||
|
||||
- Run `bazelisk clean` to force cached state to be rebuilt when environment
|
||||
changes occur.
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
name: GitHub CLI usage
|
||||
description:
|
||||
Instructions for using the `gh` command to query and inspect GitHub state
|
||||
safely.
|
||||
---
|
||||
|
||||
# GitHub CLI usage
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
This skill provides instructions for using the GitHub CLI (`gh`) to query,
|
||||
inspect, and search GitHub state (issues, pull requests, repositories) for the
|
||||
Carbon project.
|
||||
|
||||
## Safety First: Read-Only Usage
|
||||
|
||||
> [!IMPORTANT] AI assistants MUST NOT use the `gh` tool to modify any GitHub
|
||||
> project state. Do NOT run commands that create, edit, delete, label, comment
|
||||
> on, or merge issues, pull requests, releases, or any other resources.
|
||||
|
||||
### Allowed Verbs
|
||||
|
||||
- `list`
|
||||
- `view`
|
||||
- `search`
|
||||
- `status`
|
||||
- `api` (Only with `GET` requests)
|
||||
|
||||
### Prohibited Verbs
|
||||
|
||||
- `create`
|
||||
- `edit`
|
||||
- `delete`
|
||||
- `merge`
|
||||
- `reopen`
|
||||
- `close`
|
||||
- `comment`
|
||||
- `label`
|
||||
|
||||
## Repository Configuration
|
||||
|
||||
The `gh` tool interacts with a default repository when run within a local check
|
||||
out. For this project, the default repository is expected to be
|
||||
`carbon-language/carbon-lang`.
|
||||
|
||||
### Verifying Default Repository
|
||||
|
||||
To verify the current default repository configuration:
|
||||
|
||||
```bash
|
||||
gh repo view
|
||||
```
|
||||
|
||||
The output should indicate the repository is `carbon-language/carbon-lang`.
|
||||
|
||||
### Correcting Misconfigurations
|
||||
|
||||
If the default repository is misconfigured (for example, pointing to a personal
|
||||
fork or a different repository), the human operator must correct it.
|
||||
|
||||
> [!IMPORTANT] AI Assistants MUST NOT attempt to mutate `gh` configuration or
|
||||
> run commands that change the default repository (such as
|
||||
> `gh repository set-default`).
|
||||
|
||||
Instruct the human operator to run the following command to select the correct
|
||||
default repository:
|
||||
|
||||
```bash
|
||||
gh repo set-default
|
||||
```
|
||||
|
||||
The operator will be prompted to select the correct repository (e.g.,
|
||||
`carbon-language/carbon-lang`) from the available remotes.
|
||||
|
||||
## Common Query Commands
|
||||
|
||||
### Issues
|
||||
|
||||
- **List issues**: `gh issue list`
|
||||
- **View specific issue**: `gh issue view <number>`
|
||||
- **Search issues**: `gh issue search "<query>"`
|
||||
- Example: `gh issue search "crash" --state open`
|
||||
|
||||
### Pull Requests
|
||||
|
||||
- **List PRs**: `gh pr list`
|
||||
- **View specific PR**: `gh pr view <number>`
|
||||
- **View PR diff**: `gh pr diff <number>`
|
||||
- **Check PR status**: `gh pr status`
|
||||
|
||||
### Search
|
||||
|
||||
- **Search code**: `gh search code "<query>"`
|
||||
- **Search repositories**: `gh search repos "<query>"`
|
||||
|
||||
## Advanced Usage: GitHub API
|
||||
|
||||
For queries that are not supported by standard `gh` commands, you can use the
|
||||
`gh api` command to query the GitHub REST or GraphQL APIs.
|
||||
|
||||
### REST API
|
||||
|
||||
Query the REST API using paths relative to the API root.
|
||||
|
||||
- **List contributors**:
|
||||
|
||||
```bash
|
||||
gh api repos/carbon-language/carbon-lang/contributors
|
||||
```
|
||||
|
||||
- **List issue comments**:
|
||||
|
||||
```bash
|
||||
gh api repos/carbon-language/carbon-lang/issues/<issue_number>/comments
|
||||
```
|
||||
|
||||
### GraphQL API
|
||||
|
||||
For complex queries, use GraphQL to fetch exactly the data needed.
|
||||
|
||||
- **Get repository information**:
|
||||
|
||||
```bash
|
||||
gh api graphql -f query='
|
||||
query {
|
||||
repository(owner: "carbon-language", name: "carbon-lang") {
|
||||
description
|
||||
stargazerCount
|
||||
}
|
||||
}
|
||||
'
|
||||
```
|
||||
|
||||
### Pagination
|
||||
|
||||
Use the `--paginate` flag to automatically fetch all pages of results.
|
||||
|
||||
```bash
|
||||
gh api --paginate repos/carbon-language/carbon-lang/issues
|
||||
```
|
||||
|
||||
### Filtering and Formatting
|
||||
|
||||
Use `--json` to request JSON output, and `--jq` or `--template` to filter or
|
||||
format the results.
|
||||
|
||||
- **List PR titles and authors**:
|
||||
|
||||
```bash
|
||||
gh pr list --json title,author --jq '.[] | "\(.title) by \(.author.login)"'
|
||||
```
|
||||
|
||||
- **Format with Go templates**:
|
||||
|
||||
```bash
|
||||
gh issue list --template '{{range .}}{{.number}} - {{.title}}{{"\n"}}{{end}}'
|
||||
```
|
||||
|
||||
## Documentation References
|
||||
|
||||
- **GitHub CLI Manual**:
|
||||
[cli.github.com/manual](https://cli.github.com/manual/)
|
||||
- **GitHub REST API Documentation**:
|
||||
[docs.github.com/en/rest](https://docs.github.com/en/rest)
|
||||
- **GitHub GraphQL API Documentation**:
|
||||
[docs.github.com/en/graphql](https://docs.github.com/en/graphql)
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
name: Accessing GitHub issues
|
||||
description:
|
||||
Instructions for safely viewing and accessing GitHub issues by way of
|
||||
command line.
|
||||
---
|
||||
|
||||
# Accessing GitHub issues
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
This skill provides instructions for AI assistants on how to access and view
|
||||
GitHub issues. Agents should strongly prefer using the command line `gh` tool to
|
||||
access and view the contents of issues rather than viewing their contents by way
|
||||
of a web browser.
|
||||
|
||||
## Safety First
|
||||
|
||||
> [!IMPORTANT] AI assistants MUST NOT modify any GitHub issue state. Only use
|
||||
> read-only access commands like `view` or `list`. Do NOT comment, edit, create,
|
||||
> close, or delete issues.
|
||||
|
||||
## Accessing Issues
|
||||
|
||||
Agents must use this skill to access issues regardless of how they are mentioned
|
||||
(for example, by URL or by issue number).
|
||||
|
||||
### Basic View
|
||||
|
||||
To view an issue in the current default repository (expected to be Carbon):
|
||||
|
||||
```bash
|
||||
gh issue view <issue_number>
|
||||
```
|
||||
|
||||
### Including Full Context (All Comments)
|
||||
|
||||
To ensure the view includes the entire context of the issue, always include the
|
||||
`--comments` flag to dump all comments:
|
||||
|
||||
```bash
|
||||
gh issue view <issue_number> --comments
|
||||
```
|
||||
|
||||
> [!TIP] If the issue is extremely large and comments are truncated, or you need
|
||||
> to process comments programmatically, use the JSON output with `jq`:
|
||||
>
|
||||
> ```bash
|
||||
> gh issue view <issue_number> --json comments --jq '.comments[].body'
|
||||
> ```
|
||||
|
||||
### Accessing Issues in Other Repositories
|
||||
|
||||
To view an issue in another repository (for example, LLVM), use the `-R` or
|
||||
`--repo` flag to specify the repository:
|
||||
|
||||
```bash
|
||||
gh issue view <issue_number> -R <owner>/<repo> --comments
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- **LLVM Issue**:
|
||||
|
||||
```bash
|
||||
gh issue view 5678 -R llvm/llvm-project --comments
|
||||
```
|
||||
|
||||
- **Carbon Issue (Explicit)**:
|
||||
|
||||
```bash
|
||||
gh issue view 1234 -R carbon-language/carbon-lang --comments
|
||||
```
|
||||
|
||||
## Mentions via URL
|
||||
|
||||
If an issue is mentioned via URL, parse the URL to extract the repository owner,
|
||||
repository name, and issue number.
|
||||
|
||||
- **URL pattern**: `https://github.com/<owner>/<repo>/issues/<number>`
|
||||
- **Extraction**:
|
||||
- Host: `github.com`
|
||||
- Owner: `<owner>`
|
||||
- Repo: `<repo>`
|
||||
- Number: `<number>`
|
||||
|
||||
Run the command specifying the repository:
|
||||
|
||||
```bash
|
||||
gh issue view <number> -R <owner>/<repo> --comments
|
||||
```
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
name: Summarize testdata changes
|
||||
description:
|
||||
Instructions for summarizing changes to Carbon testdata files
|
||||
(`toolchain/*/testdata`).
|
||||
---
|
||||
|
||||
# Summarize testdata changes
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
This skill provides instructions for creating a comprehensive report summarizing
|
||||
changes to Carbon testdata files (`toolchain/*/testdata`) and associating them
|
||||
with related code changes.
|
||||
|
||||
## Goals
|
||||
|
||||
Produce a report that:
|
||||
|
||||
1. Summarizes code changes outside of testdata.
|
||||
2. Groups similar testdata changes together, listing all affected files for
|
||||
each group. **Every change to testdata must be represented by at least one
|
||||
group. This includes changes to CHECK lines.**
|
||||
3. Provides detailed breakdowns of test input changes and diagnostic output
|
||||
changes in the corresponding group. **Every single change to inputs or to
|
||||
STDERR checks must be explicitly mentioned in the group, with either an
|
||||
inline diff or a link to the file.**
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Identify Changes
|
||||
|
||||
Use your VCS (Git or Jujutsu) or query Github to identify changes. For large
|
||||
changes, it is recommended to use the included helper script to extract test
|
||||
input changes.
|
||||
|
||||
#### For Git Users:
|
||||
|
||||
- **Summarize code changes**: `git diff --stat -- ':!toolchain/*/testdata'`
|
||||
- To see content of non-testdata changes:
|
||||
`git diff -- ':!toolchain/*/testdata'`
|
||||
- **Identify testdata changes**: `git diff --name-only 'toolchain/*/testdata'`
|
||||
|
||||
#### For Jujutsu (jj) Users:
|
||||
|
||||
- **Summarize code changes**:
|
||||
`jj --no-pager diff --stat '~toolchain/*/testdata'`
|
||||
- Note: Quoting the fileset `'~toolchain/*/testdata'` is critical if it
|
||||
contains wildcards.
|
||||
- To see content of non-testdata changes, use `--git` to get standard
|
||||
unified diff format: `jj --no-pager diff --git '~toolchain/*/testdata'`
|
||||
- **Identify testdata changes**:
|
||||
`jj --no-pager diff --name-only 'toolchain/*/testdata'`
|
||||
|
||||
#### For Github Pull Requests:
|
||||
|
||||
- **Summarize code changes**: `gh pr diff`
|
||||
- **Identify testdata changes**:
|
||||
`gh pr diff --name-only | grep '^toolchain/.*/testdata'`
|
||||
|
||||
#### Handling Specific Revisions:
|
||||
|
||||
If you are summarizing changes in a specific revision (for example, `@-`) or
|
||||
pull request (for example, #1234), add `-r <rev>` or `<pr_number>` to the
|
||||
commands:
|
||||
|
||||
- `git diff <rev>^ <rev> ...` (or use `git show <rev>`)
|
||||
- `jj --no-pager diff -r <rev> ...`
|
||||
- `gh pr diff <pr_number>`
|
||||
|
||||
### 2. Extract Test Input Changes (Recommended)
|
||||
|
||||
To easily identify changes, use the included Python helper script to extract all
|
||||
text additions and removals from the diff, categorized by Input, STDERR, and
|
||||
STDOUT changes. This script reads a unified diff from stdin.
|
||||
|
||||
```bash
|
||||
# For Git:
|
||||
git diff -- 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
|
||||
|
||||
# For Jujutsu (jj):
|
||||
jj diff --git 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
|
||||
|
||||
# For a specific revision with jj:
|
||||
jj diff -r @- --git 'toolchain/*/testdata' | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
|
||||
|
||||
# For a specific PR with Github:
|
||||
gh pr diff 1234 | python3 .agents/skills/summarize_testdata_changes/scripts/parse_diff.py
|
||||
```
|
||||
|
||||
### 3. Identify Patterns and Produce a List of Groups
|
||||
|
||||
- Read the diff and produce a list of groups of changes that share a common
|
||||
theme or cause (for example, "Updated expected output for integer literals",
|
||||
"Added tests for new keyword").
|
||||
- **CRITICAL**: _Every single change_ in the testdata diff must be represented
|
||||
by at least one group. Do not ignore changes to `CHECK` lines.
|
||||
- If it's not clear what group a change belongs to, create a new group for
|
||||
it.
|
||||
- For each group:
|
||||
- Provide a brief description of the group.
|
||||
- (Optional) Briefly note if the group appears to be an intended or
|
||||
unintended consequence of the code changes.
|
||||
- Divide the groups into sections:
|
||||
- Test Changes: Changes to test inputs (lines not prefixed with
|
||||
`// CHECK`), along with diagnostic output changes where relevant
|
||||
- Diagnostic Changes: Changes to diagnostic output (lines prefixed with
|
||||
`// CHECK:STDERR`) with no corresponding changes to test inputs
|
||||
- [Output Type] Changes: Changes to STDOUT (lines prefixed with `// CHECK:STDOUT`)
|
||||
- Create one section for each relevant kind of test. For example,
|
||||
parser tests should typically be in a "Parse Tree Changes" section,
|
||||
check tests should typically be in a "SemIR Changes" section, and
|
||||
lower tests should typically be in an "LLVM IR Changes" section.
|
||||
|
||||
### 4. Improve Grouping
|
||||
|
||||
- Read the list of groups and check to see if any of them should be combined
|
||||
or split apart. If needed, do so.
|
||||
|
||||
### 5. Assign Changes to Groups
|
||||
|
||||
- Read the diff again, and then for _each_ change in the diff:
|
||||
- Add the change to the appropriate group (or, rarely, groups).
|
||||
- **CRITICAL**: _Every single change_ in the testdata diff must be
|
||||
represented by at least one group. Do not ignore changes to `CHECK`
|
||||
lines.
|
||||
- If the change affects _test inputs_ (lines not prefixed with `// CHECK`)
|
||||
or _diagnostic output_ (lines prefixed with `// CHECK:STDERR`):
|
||||
- List the file within the group. Don't just give one or a few
|
||||
examples. Include every file.
|
||||
- Provide an inline diff if the change is small.
|
||||
- Provide a link to the file if the change is large.
|
||||
- Otherwise, if the change only affects _STDOUT_ (lines prefixed with
|
||||
`// CHECK:STDOUT`):
|
||||
- Ensure the group contains a representative example that matches the
|
||||
current change.
|
||||
- The representative example should be an inline diff of the change.
|
||||
- **CRITICAL**: _Every single change_ to test inputs and diagnostic
|
||||
outputs in the files being summarized must be explicitly listed in at
|
||||
least one group. Do not skip changes, even if they are similar to
|
||||
changes you've already seen, and do not just give examples.
|
||||
|
||||
### 6. Validation
|
||||
|
||||
As a final validation step:
|
||||
|
||||
- Read through the testdata diff again.
|
||||
- Ensure that every change in the diff is reflected by at least one group in
|
||||
the report.
|
||||
|
||||
## Report Template
|
||||
|
||||
Use the following template for the generated report:
|
||||
|
||||
```markdown
|
||||
# `testdata` Change Summary
|
||||
|
||||
## Code Changes
|
||||
|
||||
[One paragraph summarizing changes outside of testdata.]
|
||||
|
||||
## Test Changes
|
||||
|
||||
### [Group Name]
|
||||
|
||||
[Description of the group.]
|
||||
|
||||
[Change 1: diff context OR link]
|
||||
|
||||
[Change 2: diff context OR link]
|
||||
|
||||
...
|
||||
|
||||
## Diagnostic Changes
|
||||
|
||||
### [Group Name]
|
||||
|
||||
[Description of the group.]
|
||||
|
||||
[Change 1: diff context OR link]
|
||||
|
||||
[Change 2: diff context OR link]
|
||||
|
||||
...
|
||||
|
||||
## [Output Type] Changes
|
||||
|
||||
### [File Path]
|
||||
|
||||
[Description of the group.]
|
||||
|
||||
[Example diff context]
|
||||
|
||||
Changes of this kind were found in [Number] files. Examples: [List of files]
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
Skip sections that would be empty.
|
||||
@@ -0,0 +1,65 @@
|
||||
__copyright__ = """
|
||||
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
|
||||
"""
|
||||
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import TextIO, Dict, List
|
||||
|
||||
|
||||
def parse_diff(stream: TextIO) -> None:
|
||||
current_file: str = ""
|
||||
file_changes: Dict[str, Dict[str, List[str]]] = defaultdict(
|
||||
lambda: {"input": [], "stderr": [], "stdout": []}
|
||||
)
|
||||
|
||||
for line in stream:
|
||||
if line.startswith("diff --git"):
|
||||
parts = line.split()
|
||||
if len(parts) >= 4:
|
||||
current_file = (
|
||||
parts[3][2:] if parts[3].startswith("b/") else parts[3]
|
||||
)
|
||||
elif line.startswith("+") or line.startswith("-"):
|
||||
if not line.startswith("+++") and not line.startswith("---"):
|
||||
stripped = line[1:].strip()
|
||||
if stripped.startswith("// CHECK:STDERR"):
|
||||
file_changes[current_file]["stderr"].append(
|
||||
line.rstrip("\n")
|
||||
)
|
||||
elif stripped.startswith("// CHECK:STDOUT"):
|
||||
file_changes[current_file]["stdout"].append(
|
||||
line.rstrip("\n")
|
||||
)
|
||||
elif stripped.startswith("// CHECK"):
|
||||
file_changes[current_file]["stdout"].append(
|
||||
line.rstrip("\n")
|
||||
)
|
||||
else:
|
||||
file_changes[current_file]["input"].append(
|
||||
line.rstrip("\n")
|
||||
)
|
||||
|
||||
for f, c in file_changes.items():
|
||||
if not c["input"] and not c["stderr"] and not c["stdout"]:
|
||||
continue
|
||||
print(f"File: {f}")
|
||||
if c["input"]:
|
||||
print(" --- Input Changes ---")
|
||||
for change in c["input"]:
|
||||
print(f" {change}")
|
||||
if c["stderr"]:
|
||||
print(" --- STDERR Changes ---")
|
||||
for change in c["stderr"]:
|
||||
print(f" {change}")
|
||||
if c["stdout"]:
|
||||
print(" --- STDOUT Changes ---")
|
||||
for change in c["stdout"]:
|
||||
print(f" {change}")
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parse_diff(sys.stdin)
|
||||
@@ -52,9 +52,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
### Updating test data
|
||||
|
||||
Carbon tests often use `file_test` (for example,
|
||||
`//toolchain/testing/file_test`). If you change compiler behavior, you likely
|
||||
need to update expected test outputs. **Do not manually edit thousands of lines
|
||||
of expected output.** Use the script:
|
||||
`//toolchain/testing/file_test`). For detailed guidelines on authoring tests,
|
||||
including file splits, naming conventions (`fail_`, `todo_`), and generating
|
||||
minimal output with SemIR dumps, please refer to the **Toolchain tests** skill.
|
||||
|
||||
If you change compiler behavior, you likely need to update expected test
|
||||
outputs. **Do not manually edit thousands of lines of expected output.** Use the
|
||||
script:
|
||||
|
||||
```bash
|
||||
./toolchain/autoupdate_testdata.py
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
name: Toolchain tests
|
||||
description:
|
||||
Instructions for authoring, structuring, and running toolchain tests using
|
||||
the file_test infrastructure.
|
||||
---
|
||||
|
||||
# Toolchain tests
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
|
||||
## Introduction
|
||||
|
||||
This skill provides guidelines and patterns for creating and updating tests for
|
||||
the Carbon toolchain, especially file tests in `toolchain/*/testdata/` (for
|
||||
example, `toolchain/check/testdata/`).
|
||||
|
||||
Toolchain tests evaluate Carbon source files through Lexing, Parsing, Checking,
|
||||
and optionally Lowering. Output (for example SemIR dumps, Clang errors) is
|
||||
captured and validated using inline CHECK records.
|
||||
|
||||
## Structure and Authoring
|
||||
|
||||
### File Layout and Headers
|
||||
|
||||
Test files must start with the standard Carbon license, followed by
|
||||
configuration comments. Separate sections with blank comment lines (`//`).
|
||||
|
||||
```carbon
|
||||
// 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
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/...
|
||||
//
|
||||
// AUTOUPDATE
|
||||
```
|
||||
|
||||
- `// AUTOUPDATE` is mandatory for files using CHECK markers.
|
||||
- `// TIP:` lines are automatically generated by the autoupdater. You do not
|
||||
need to hand-write them. It is harmless to add them, but the script will
|
||||
handle it.
|
||||
|
||||
### Minimized Preludes
|
||||
|
||||
When writing tests entirely unrelated to the Core package, specify a minimal
|
||||
prelude file using `// INCLUDE-FILE`. Usually, include
|
||||
`toolchain/testing/testdata/min_prelude/` scripts, such as `int.carbon` or
|
||||
`primitives.carbon`. This significantly speeds up execution and minimizes STDOUT
|
||||
noise.
|
||||
|
||||
### Split Tests and `[[@TEST_NAME]]`
|
||||
|
||||
A single physical file can test multiple scenarios using split constraints:
|
||||
|
||||
```carbon
|
||||
// --- passing_case.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// ...
|
||||
|
||||
// --- fail_bad_case.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
// ...
|
||||
```
|
||||
|
||||
- Use `library "[[@TEST_NAME]]";` in each split when necessary to prevent name
|
||||
conflicts or redefining the default library.
|
||||
- Exactly `[[@TEST_NAME]]` (including the brackets) should be used. The test
|
||||
infrastructure automatically replaces it with the split's filename minus
|
||||
`todo_` and `fail_` prefixes.
|
||||
- **Do not put code that is expected to pass and code that is expected to fail
|
||||
into the same split.** Validation relies on non-failing splits producing
|
||||
absolutely no errors and failing splits producing the correct compiler
|
||||
errors independently.
|
||||
|
||||
### File Prefixing: `fail_` and `todo_`
|
||||
|
||||
Expected failures must be differentiated from unexpected failures (and from
|
||||
bugs). Include prefixes to name individual split files or the main test:
|
||||
|
||||
- `fail_...`: The test should and does produce compiler errors.
|
||||
- `todo_fail_...`: The test should produce errors but currently does not.
|
||||
- `fail_todo_...`: The test does produce errors or crashes, but it shouldn't
|
||||
(or produces the wrong errors or otherwise misbehaves with errors).
|
||||
- `todo_...`: The test has some incorrect behavior, but doesn't produce errors
|
||||
currently, and shouldn't.
|
||||
|
||||
**Main File Naming**: The main test file (and any split-files) must have a
|
||||
`fail_` prefix if they have an associated error. **Exception**: The main file
|
||||
may omit `fail_` if it contains a least one split that has a `fail_` prefix.
|
||||
|
||||
Both the `fail_` and `todo_` prefixes are stripped from filename properties like
|
||||
`[[@TEST_NAME]]`.
|
||||
|
||||
### Test Code Comments
|
||||
|
||||
- **No agent thinking:** Do not include comments describing your reasoning or
|
||||
"train of thought" (for example, "Wait, but...") inside the test files. Any
|
||||
comments left in tests should be concise and describe what the test _itself_
|
||||
is validating for human readers.
|
||||
|
||||
## SemIR Dumps and Minimizing Output
|
||||
|
||||
Limit STDOUT checks to the logic under test. Always use `//@dump-sem-ir-begin`
|
||||
and `//@dump-sem-ir-end` around the specific declarations/blocks where SemIR
|
||||
output is desired. Only use these markers and **not**
|
||||
`--dump-sem-ir-ranges=if-present` or similar extra args—new tests use
|
||||
`//@dump-sem-ir...` to naturally filter output to the highlighted segments based
|
||||
on the default behavior.
|
||||
|
||||
```carbon
|
||||
//@dump-sem-ir-begin
|
||||
fn F(x:? form(ref i32));
|
||||
//@dump-sem-ir-end
|
||||
```
|
||||
|
||||
## Creating/Updating the Output
|
||||
|
||||
AI tools should **never** hand-write or manually touch `// CHECK:STDOUT:` or
|
||||
`// CHECK:STDERR:` comments.
|
||||
|
||||
Write your Carbon test code, headers, and `// AUTOUPDATE` then run the test
|
||||
updater:
|
||||
|
||||
```bash
|
||||
./toolchain/autoupdate_testdata.py toolchain/PATH/TO/YOUR/TEST.carbon
|
||||
```
|
||||
|
||||
Review the updated test outputs (for example, by way of `git diff`). Ensure
|
||||
logic paths are correctly tested rather than producing massive boilerplate
|
||||
blocks.
|
||||
@@ -38,11 +38,9 @@ common:non-fatal-checks --per_file_copt=common/check_internal.cpp@-DCARBON_NON_F
|
||||
# enable use of the target config here to make our build and tests more
|
||||
# efficient, see the documentation in //bazel/carbon_rules/BUILD for details.
|
||||
common --flag_alias=use_target_config_carbon_rules=//bazel/carbon_rules:use_target_config_carbon_rules
|
||||
common --flag_alias=use_target_config_runtimes_builder=//toolchain/driver:use_target_config_runtimes_builder
|
||||
# Bazel doesn't track what commands the flag_alias is valid for, so we can't use
|
||||
# common here.
|
||||
build --use_target_config_carbon_rules
|
||||
build --use_target_config_runtimes_builder
|
||||
|
||||
# Default to using a disk cache to minimize re-building LLVM and Clang which we
|
||||
# try to avoid updating too frequently to minimize rebuild cost. The location
|
||||
@@ -87,12 +85,12 @@ common --define=absl=1
|
||||
# Enable TCMalloc on Linux in optimized builds.
|
||||
common --custom_malloc=//bazel/malloc:tcmalloc_if_linux_opt
|
||||
|
||||
# Configuration for enabling Address Sanitizer. Note that this is enabled by
|
||||
# default for fastbuild. The config is provided to enable ASan even in
|
||||
# optimized or other build configurations. Note that ASan and TCMalloc are
|
||||
# Configuration for enabling Address Sanitizer. Note that ASan and TCMalloc are
|
||||
# incompatible so this explicitly forces the system malloc.
|
||||
common:asan --features=asan
|
||||
common:asan --custom_malloc=@bazel_tools//tools/cpp:malloc
|
||||
# Also double the test timeouts for ASan to improve their consistency.
|
||||
test:asan --test_timeout=120,600,1800,-1
|
||||
|
||||
# Configuration for enabling LibFuzzer (along with ASan).
|
||||
common:fuzzer --features=fuzzer
|
||||
@@ -123,6 +121,11 @@ common:linux --define=pfm=1
|
||||
# Disables `actions.declare_symlink`. Done for cross-environment support.
|
||||
common --allow_unresolved_symlinks=false
|
||||
|
||||
# Removes the leading `/proc/self/cwd/` from file paths in the debug info. Some
|
||||
# tools like VS Code don't understand `/proc/self/cwd` in places like terminal
|
||||
# stack dumps, but do understand paths relative to the workspace root.
|
||||
common --copt=-fdebug-prefix-map=/proc/self/cwd=
|
||||
|
||||
# Allow users to override any of the flags desired by importing a user-specific
|
||||
# RC file here if present.
|
||||
try-import %workspace%/user.bazelrc
|
||||
|
||||
@@ -11,6 +11,7 @@ DerivePointerAlignment: 'false'
|
||||
ExperimentalAutoDetectBinPacking: 'false'
|
||||
FixNamespaceComments: 'true'
|
||||
InsertBraces: 'true'
|
||||
InsertTrailingCommas: None
|
||||
PointerAlignment: Left
|
||||
# We abuse control macros for formatting other kinds of macros.
|
||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||
@@ -26,3 +27,8 @@ StatementMacros: ['ABSTRACT']
|
||||
QualifierAlignment: Custom
|
||||
QualifierOrder:
|
||||
[inline, static, friend, constexpr, const, volatile, restrict, type]
|
||||
Macros:
|
||||
# These macros can contain variable declarations, so clang-format needs to
|
||||
# "see through" them in order to format them correctly.
|
||||
- CARBON_ASSIGN_OR_RETURN(x)=x
|
||||
- CARBON_KIND(x)=x
|
||||
|
||||
@@ -70,6 +70,13 @@ runs:
|
||||
build --remote_cache=https://storage.googleapis.com/carbon-builds-github-v${CACHE_VERSION}
|
||||
build --remote_download_outputs=minimal
|
||||
|
||||
# Allow passing targets that are incompatible so that our explicit
|
||||
# target lists work more like //... wild card patterns in CI. In CI,
|
||||
# we're using explicit target lists to prune to a minimal set of
|
||||
# dependencies, and so skipping incompatible targets is the expected
|
||||
# behavior.
|
||||
build --skip_incompatible_explicit_targets
|
||||
|
||||
# We import a special key into every action in order to key the Bazel
|
||||
# remote cache in a way that avoids collisions between different
|
||||
# runners. Anything that might change the system external to Bazel but
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# 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
|
||||
|
||||
name: Check Dependent Label
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, labeled, unlabeled]
|
||||
|
||||
# This workflow runs as `pull_request_target` so that the check can't be
|
||||
# disabled or bypassed by a the PR, but it doesn't need any permissions.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
check_label:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
# prettier-ignore
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
|
||||
- name: Check for 'dependent' label
|
||||
run: |
|
||||
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'dependent') }}" == "true" ]]; then
|
||||
echo "PR has 'dependent' label. Blocking merge."
|
||||
exit 1
|
||||
fi
|
||||
echo "PR does not have 'dependent' label."
|
||||
@@ -75,10 +75,6 @@ jobs:
|
||||
if: steps.filter.outputs.has_cpp == 'true'
|
||||
run: ./scripts/create_compdb.py
|
||||
|
||||
- name: Build deps for clangd-tidy
|
||||
if: steps.filter.outputs.has_cpp == 'true'
|
||||
run: ./scripts/run_bazel.py build //scripts:deps_for_clangd_tidy
|
||||
|
||||
- name: Install clangd-tidy
|
||||
if: steps.filter.outputs.has_cpp == 'true'
|
||||
run: pip install clangd-tidy==1.1.0.post2
|
||||
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
test -c opt --stamp --remote_download_toplevel \
|
||||
--pre_release=nightly --nightly_date=${{ env.nightly_date }} \
|
||||
//toolchain \
|
||||
//toolchain/install:carbon_toolchain_tar_gz_rule \
|
||||
//toolchain/install:carbon_toolchain_tar_gz \
|
||||
//toolchain/install:carbon_toolchain_tar_gz_test
|
||||
|
||||
- name: Extract the release version
|
||||
|
||||
@@ -28,8 +28,7 @@ jobs:
|
||||
# Only generate suggestions if pre-commit for a PR failed.
|
||||
if: |
|
||||
github.event.workflow_run.conclusion == 'failure' &&
|
||||
github.event.workflow_run.event == 'pull_request' &&
|
||||
github.actor != 'jonmeow'
|
||||
github.event.workflow_run.event == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
|
||||
@@ -22,13 +22,30 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name:
|
||||
Testing ${{ matrix.config.name != 'Default' && format('({0})',
|
||||
matrix.config.name) || '' }} (${{ matrix.runner }})
|
||||
strategy:
|
||||
matrix:
|
||||
# Test a recent version of each supported OS.
|
||||
runner: ['ubuntu-22.04', 'macos-14']
|
||||
build_mode: [fastbuild, opt]
|
||||
# Create a synthetic matrix dimension with the event name for filtering.
|
||||
event: ['${{ github.event_name }}']
|
||||
config:
|
||||
- name: 'Default'
|
||||
flags: ''
|
||||
- name: 'Opt'
|
||||
flags: '-c opt'
|
||||
- name: 'ASan'
|
||||
flags: '--config=asan'
|
||||
exclude:
|
||||
- runner: 'macos-14'
|
||||
config: { name: 'ASan', flags: '--config=asan' }
|
||||
- event: 'pull_request'
|
||||
config: { name: 'ASan', flags: '--config=asan' }
|
||||
- event: 'merge_group'
|
||||
config: { name: 'ASan', flags: '--config=asan' }
|
||||
runs-on: ${{ matrix.runner }}
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@58077d3c7e43986b6b15fba718e8ea69e387dfcc # v2.15.1
|
||||
@@ -74,7 +91,7 @@ jobs:
|
||||
targets_file: ${{ runner.temp }}/targets
|
||||
|
||||
# Build and run just the tests impacted by the PR or merge group.
|
||||
- name: Test (${{ matrix.build_mode }})
|
||||
- name: Test (${{ matrix.config.name }})
|
||||
if: steps.test-setup.outputs.has_code == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
@@ -85,10 +102,11 @@ jobs:
|
||||
run: |
|
||||
# Decrease the jobs sharply if we see repeated failures to try to
|
||||
# work around transient network errors even if it makes things
|
||||
# slower.
|
||||
# slower. Note that we allow passing targets that are incompatible and
|
||||
# skip thim as-if we were using `//...` style wild card patterns.
|
||||
./scripts/run_bazel.py \
|
||||
--attempts=5 --jobs-on-last-attempt=4 \
|
||||
test -c ${{ matrix.build_mode }} \
|
||||
test ${{ matrix.config.flags }} \
|
||||
--target_pattern_file=$TARGETS_FILE
|
||||
|
||||
# See "Disk space before build" in `test-setup`.
|
||||
|
||||
@@ -33,6 +33,11 @@ repos:
|
||||
rev: efaea7c61c774c0b1a9805fd999e754a2d19dbd1 # frozen: v1.2.5
|
||||
hooks:
|
||||
- id: check-google-doc-style
|
||||
exclude: |
|
||||
(?x)^(
|
||||
.*\.agents/.*|
|
||||
.*AGENTS.md
|
||||
)$
|
||||
- id: markdown-toc
|
||||
- repo: local
|
||||
hooks:
|
||||
|
||||
@@ -14,6 +14,7 @@ assistant, **read this first** to avoid common pitfalls.
|
||||
|
||||
- [General instructions](#general-instructions)
|
||||
- [Project structure](#project-structure)
|
||||
- [Bazel usage](#bazel-usage)
|
||||
- [Toolchain development](#toolchain-development)
|
||||
|
||||
## General instructions
|
||||
@@ -40,6 +41,12 @@ assistant, **read this first** to avoid common pitfalls.
|
||||
See the "Tool usage" skill for instructions on what tools to use in the
|
||||
carbon-lang project.
|
||||
|
||||
## Bazel usage
|
||||
|
||||
> [!IMPORTANT] Always use `bazelisk` instead of `bazel` for all commands in the
|
||||
> Carbon project. Refer to the
|
||||
> [Bazel usage skill](/.agents/skills/bazel/SKILL.md) for detailed instructions.
|
||||
|
||||
## Code style
|
||||
|
||||
See the "Code style" skill for instructions on formatting, style guides, and
|
||||
@@ -2,6 +2,8 @@
|
||||
# Exceptions. See /LICENSE for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "bool_setting", "int_flag")
|
||||
|
||||
filegroup(
|
||||
name = "clang_tidy_config",
|
||||
srcs = [".clang-tidy"],
|
||||
@@ -13,3 +15,38 @@ alias(
|
||||
name = "generate_compile_commands",
|
||||
actual = "@wolfd_bazel_compile_commands//:generate_compile_commands",
|
||||
)
|
||||
|
||||
bool_setting(
|
||||
name = "runtimes_build",
|
||||
build_setting_default = False,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
int_flag(
|
||||
name = "bootstrap_stage",
|
||||
build_setting_default = 0,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# A setting that causes bootstrapping to occur using the `exec` config rather
|
||||
# than the target config.
|
||||
#
|
||||
# The exec config is the more technically correct way of doing bootstrapping
|
||||
# than the target config. For example it allows bootstrapping with a target that
|
||||
# isn't compatible with the current execution host. However, in development
|
||||
# builds, it is likely to force building the entire toolchain twice -- once in
|
||||
# the target config for running test, and a second time in the exec config for
|
||||
# the bootstrap. As a consequence, this is disabled by default.
|
||||
#
|
||||
# TODO: Add documentation for using the bootstrap flags once stabilized.
|
||||
bool_flag(
|
||||
name = "bootstrap_exec_config",
|
||||
build_setting_default = False,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "bootstrap_with_exec_config",
|
||||
flag_values = {"//:bootstrap_exec_config": "True"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
+10
-2
@@ -33,6 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Style](#style)
|
||||
- [Google Docs and Markdown](#google-docs-and-markdown)
|
||||
- [Other files](#other-files)
|
||||
- [Testing](#testing)
|
||||
- [License](#license)
|
||||
- [Google Docs](#google-docs)
|
||||
- [Markdown](#markdown)
|
||||
@@ -232,7 +233,7 @@ as well as helpful tooling that will ease the contribution process. For example,
|
||||
#### Using AI-based contribution tools
|
||||
|
||||
If you are using an AI assistant to help you contribute, or if you are an AI
|
||||
assistant yourself, please consult [GEMINI.md](/GEMINI.md) for high-density
|
||||
assistant yourself, please consult [AGENTS.md](/AGENTS.md) for high-density
|
||||
technical context and tips.
|
||||
|
||||
All submissions to Carbon need to follow our
|
||||
@@ -308,7 +309,6 @@ follow the Carbon documentation and coding styles.
|
||||
request.
|
||||
|
||||
- For code:
|
||||
|
||||
- New features should have a documented design that has been approved
|
||||
through the [evolution process](docs/project/evolution.md). This
|
||||
includes modifications to preexisting designs.
|
||||
@@ -427,6 +427,14 @@ Other style points to be aware of are:
|
||||
|
||||
If you're not sure what style to use, please ask on Discord or GitHub.
|
||||
|
||||
## Testing
|
||||
|
||||
Most development in the Carbon project is on the [toolchain](toolchain/). The
|
||||
toolchain contains some unit tests, but the majority of testing is done through
|
||||
`file_test` tests, which test the output of the different phases of the
|
||||
toolchain. The toolchain docs include instructions for
|
||||
[building and running tests](toolchain/docs/adding_features.md#tests-and-debugging).
|
||||
|
||||
## License
|
||||
|
||||
A license is required at the top of all documents and files.
|
||||
|
||||
+11
-12
@@ -40,6 +40,14 @@ bazel_dep(name = "tcmalloc", version = "0.0.0-20250927-12f2552")
|
||||
bazel_dep(name = "tree-sitter-bazel", version = "0.26.5")
|
||||
|
||||
bazel_dep(name = "wolfd_bazel_compile_commands", version = "0.5.2", dev_dependency = True)
|
||||
git_override(
|
||||
module_name = "wolfd_bazel_compile_commands",
|
||||
# This is https://github.com/wolfd/bazel-compile-commands/pull/3 which is
|
||||
# needed to correctly select target configurations in our compile commands.
|
||||
commit = "7c673ac868cd237f262bb37a7819b1a279566a66",
|
||||
remote = "https://github.com/chandlerc/bazel-compile-commands.git",
|
||||
)
|
||||
|
||||
bazel_dep(name = "bazel_clang_tidy", dev_dependency = True)
|
||||
git_override(
|
||||
module_name = "bazel_clang_tidy",
|
||||
@@ -56,16 +64,7 @@ use_repo(bazel_cc_toolchain, "bazel_cc_toolchain")
|
||||
|
||||
register_toolchains("@bazel_cc_toolchain//:all")
|
||||
|
||||
# TODO: Trying out `wolfd_bazel_compile_commands`, figure out if there are
|
||||
# issues. Once people have had a chance to try and check for problems, remove
|
||||
# one or the other.
|
||||
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
|
||||
git_override(
|
||||
module_name = "hedron_compile_commands",
|
||||
# HEAD as of 2026-01-28.
|
||||
commit = "abb61a688167623088f8768cc9264798df6a9d10",
|
||||
remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git",
|
||||
)
|
||||
register_toolchains("//toolchain/install:all")
|
||||
|
||||
# Required for llvm-project.
|
||||
bazel_dep(name = "platforms", version = "1.0.0")
|
||||
@@ -84,8 +83,8 @@ git_override(
|
||||
build_file_content = "# empty",
|
||||
# We pin to specific upstream commits and try to track top-of-tree
|
||||
# reasonably closely rather than pinning to a specific release.
|
||||
# HEAD as of 2026-02-12.
|
||||
commit = "6811a83c81500ee373adfc0d9978ff9625a4cf1c",
|
||||
# HEAD as of 2026-04-01.
|
||||
commit = "b71eacea7687f68c11299e3bda5654fbbaa1e20e",
|
||||
patch_cmds = ["echo \"module(name='llvm-raw')\" > MODULE.bazel"],
|
||||
patch_strip = 1,
|
||||
patches = [
|
||||
|
||||
Generated
+1
-53
@@ -287,8 +287,6 @@
|
||||
"https://bcr.bazel.build/modules/tree-sitter-bazel/0.26.5/source.json": "cd6d21ca1b159961230ac6c33e0c7f638d7c40daa6746b4097d896441fd89074",
|
||||
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
|
||||
"https://bcr.bazel.build/modules/upb/0.0.0-20230516-61a97ef/MODULE.bazel": "c0df5e35ad55e264160417fd0875932ee3c9dda63d9fccace35ac62f45e1b6f9",
|
||||
"https://bcr.bazel.build/modules/wolfd_bazel_compile_commands/0.5.2/MODULE.bazel": "74527ae5c60c4974e5b6c9fc2f39b78af47b44559b1913324c8185ff4f2f3900",
|
||||
"https://bcr.bazel.build/modules/wolfd_bazel_compile_commands/0.5.2/source.json": "bbf0b6d7d2a2e58d238ef580fbf3072018e1c9447c9e2295b6a2b32d87a74338",
|
||||
"https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072",
|
||||
"https://bcr.bazel.build/modules/yq.bzl/0.1.1/source.json": "2d2bad780a9f2b9195a4a370314d2c17ae95eaa745cefc2e12fbc49759b15aa3",
|
||||
"https://bcr.bazel.build/modules/zlib-ng/2.0.7/MODULE.bazel": "3ca640b745b55f287e95aa0477e6cd76dfa0a565725d5412b7d8dae4274436c8",
|
||||
@@ -305,7 +303,7 @@
|
||||
"moduleExtensions": {
|
||||
"//bazel/cc_toolchains:clang_configuration.bzl%clang_toolchain_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "44/bjnoCpU6JrWf798MGhACH/QjSv0OkcCi/jF+EGg0=",
|
||||
"bzlTransitiveDigest": "H3RsK0MbgutDMSlPWTwZq4Vk1U5sjDtgJ5MXQxg7GLU=",
|
||||
"usagesDigest": "lTxkeAFhR0iBEa3dg5hWvtd2HFCr5zCJx/fl27A+IKA=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
@@ -362,56 +360,6 @@
|
||||
"recordedRepoMappingEntries": []
|
||||
}
|
||||
},
|
||||
"@@hedron_compile_commands+//:workspace_setup.bzl%hedron_compile_commands_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "GTNjt7oW5GiUBfcBtXAq5etqAZ6ue6PRd5nPaxeMoYM=",
|
||||
"usagesDigest": "CbJ2MjubH36j9xaONhhASfhodhpi5fzvuyg/IW2f7Ds=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {},
|
||||
"recordedRepoMappingEntries": [
|
||||
[
|
||||
"hedron_compile_commands+",
|
||||
"bazel_tools",
|
||||
"bazel_tools"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"@@hedron_compile_commands+//:workspace_setup_transitive.bzl%hedron_compile_commands_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "IfDf0vEa2jjQ11RNpUM0u4xftPXIs+pyM8IMVkRqVMk=",
|
||||
"usagesDigest": "yxZQbFglJyjpn7JZ9mhIc3EhLzZivlbs6wiHWOKJ/UA=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {},
|
||||
"recordedRepoMappingEntries": []
|
||||
}
|
||||
},
|
||||
"@@hedron_compile_commands+//:workspace_setup_transitive_transitive.bzl%hedron_compile_commands_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "1p58k3o2Jgjt/pBE7cb8WmmkplrSguIKma/h32x7X10=",
|
||||
"usagesDigest": "GkOuy/k8wz0dbKMeEJFKEJB3CWkMZt3DYcPgj4lALkI=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {},
|
||||
"recordedRepoMappingEntries": []
|
||||
}
|
||||
},
|
||||
"@@hedron_compile_commands+//:workspace_setup_transitive_transitive_transitive.bzl%hedron_compile_commands_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "arNWX4EleUjJxqkM5nCRTj+ce05Zz1gSdGH1DCKOoLs=",
|
||||
"usagesDigest": "WZExKK/BI4lqpUZfPpv4YARDE1Y7igQB+wYGKvNoCKs=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {},
|
||||
"recordedRepoMappingEntries": []
|
||||
}
|
||||
},
|
||||
"@@protobuf+//python/dist:system_python.bzl%system_python_extension": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "pmsA+awieucfllLc2n7k8xEoPp0i5LF9Hw6mGX0cqSQ=",
|
||||
|
||||
@@ -27,7 +27,6 @@ If you haven't received a response, a couple steps to take are (in order):
|
||||
1. Contact individuals directly:
|
||||
- [Chandler Carruth](mailto:chandlerc@gmail.com)
|
||||
- [Richard Smith](mailto:richard@metafoo.co.uk)
|
||||
- [Jon Ross-Perkins](mailto:jperkins@google.com)
|
||||
2. Reach out on
|
||||
[#infra](https://discord.com/channels/655572317891461132/707150492370862090)
|
||||
on Discord ([invite](https://discord.gg/ZjVdShJDAs))
|
||||
|
||||
+45
-23
@@ -4,20 +4,10 @@
|
||||
|
||||
"""Provides rules for building Carbon files using the toolchain."""
|
||||
|
||||
load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
|
||||
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
|
||||
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
|
||||
|
||||
def _runtimes_path(runtimes_target):
|
||||
path = None
|
||||
for f in runtimes_target:
|
||||
if f.short_path.endswith("clang_resource_dir/lib"):
|
||||
path = f.path
|
||||
break
|
||||
|
||||
if not path:
|
||||
fail("Could not find the `clang_resource_dir` in target {}".format(runtimes_target.label))
|
||||
|
||||
return path[:-len("/clang_resource_dir/lib")]
|
||||
|
||||
def _carbon_binary_impl(ctx):
|
||||
toolchain_driver = ctx.executable.internal_exec_toolchain_driver
|
||||
toolchain_data = ctx.files.internal_exec_toolchain_data
|
||||
@@ -30,10 +20,12 @@ def _carbon_binary_impl(ctx):
|
||||
toolchain_data = ctx.files.internal_target_toolchain_data
|
||||
prebuilt_runtimes = ctx.files.internal_target_prebuilt_runtimes
|
||||
|
||||
# The extra link flags needed.
|
||||
link_flags = []
|
||||
|
||||
# Pass any C++ flags from our dependencies onto Carbon.
|
||||
dep_flags = []
|
||||
dep_hdrs = []
|
||||
dep_link_flags = []
|
||||
dep_link_inputs = []
|
||||
for dep in ctx.attr.deps:
|
||||
if CcInfo in dep:
|
||||
@@ -47,15 +39,16 @@ def _carbon_binary_impl(ctx):
|
||||
dep_flags += ["--clang-arg=-isystem{0}".format(path) for path in cc_info.compilation_context.system_includes.to_list()]
|
||||
dep_hdrs.append(cc_info.compilation_context.headers)
|
||||
for link_input in cc_info.linking_context.linker_inputs.to_list():
|
||||
# TODO: `carbon link` doesn't support linker flags yet.
|
||||
# dep_link_flags += link_input.user_link_flags
|
||||
link_flags += link_input.user_link_flags
|
||||
dep_link_inputs += link_input.additional_inputs
|
||||
for lib in link_input.libraries:
|
||||
dep_link_inputs += [dep for dep in [lib.dynamic_library, lib.static_library] if dep]
|
||||
dep_link_inputs += lib.objects
|
||||
if DefaultInfo in dep:
|
||||
dep_link_inputs += dep[DefaultInfo].files.to_list()
|
||||
dep_link_flags += [dep.path for dep in dep_link_inputs]
|
||||
|
||||
# Add the dependencies' link flags and inputs to the link flags.
|
||||
link_flags += [dep.path for dep in dep_link_inputs]
|
||||
|
||||
# Build object files for the prelude and for the binary itself.
|
||||
# TODO: Eventually the prelude should be build as a separate `carbon_library`.
|
||||
@@ -89,19 +82,47 @@ def _carbon_binary_impl(ctx):
|
||||
inputs = depset(direct = srcs_reordered, transitive = dep_hdrs),
|
||||
executable = toolchain_driver,
|
||||
tools = depset(toolchain_data),
|
||||
arguments = ["compile", "--output=" + out.path] +
|
||||
arguments = ["compile", "--output=" + out.path, "--output-last-input-only"] +
|
||||
[s.path for s in srcs_reordered] + extra_flags + ctx.attr.flags,
|
||||
mnemonic = "CarbonCompile",
|
||||
progress_message = "Compiling " + src.short_path,
|
||||
)
|
||||
|
||||
# Add the Carbon object files to the link flags.
|
||||
link_flags += [o.path for o in objs]
|
||||
|
||||
bin = ctx.actions.declare_file(ctx.label.name)
|
||||
|
||||
# Get all link options from the toolchain and dependencies using standard pattern.
|
||||
cc_toolchain = ctx.attr._cc_toolchain[cc_common.CcToolchainInfo]
|
||||
feature_configuration = cc_common.configure_features(
|
||||
ctx = ctx,
|
||||
cc_toolchain = cc_toolchain,
|
||||
requested_features = ctx.features,
|
||||
unsupported_features = ctx.disabled_features,
|
||||
)
|
||||
variables = cc_common.create_link_variables(
|
||||
feature_configuration = feature_configuration,
|
||||
cc_toolchain = cc_toolchain,
|
||||
is_using_linker = True,
|
||||
user_link_flags = link_flags + [
|
||||
# TODO: Remove once the sanitizer runtimes are available.
|
||||
"-fno-sanitize=all",
|
||||
],
|
||||
output_file = bin.path,
|
||||
)
|
||||
full_link_flags = cc_common.get_memory_inefficient_command_line(
|
||||
feature_configuration = feature_configuration,
|
||||
action_name = ACTION_NAMES.cpp_link_executable,
|
||||
variables = variables,
|
||||
)
|
||||
|
||||
ctx.actions.run(
|
||||
outputs = [bin],
|
||||
inputs = objs + dep_link_inputs,
|
||||
executable = toolchain_driver,
|
||||
tools = depset(toolchain_data + prebuilt_runtimes),
|
||||
arguments = ["--prebuilt-runtimes=" + _runtimes_path(prebuilt_runtimes), "link", "--output=" + bin.path] + ["--"] + dep_link_flags + [o.path for o in objs],
|
||||
arguments = full_link_flags,
|
||||
mnemonic = "CarbonLink",
|
||||
progress_message = "Linking " + bin.short_path,
|
||||
)
|
||||
@@ -148,9 +169,10 @@ _carbon_binary_internal = rule(
|
||||
),
|
||||
"prelude_srcs": attr.label_list(allow_files = [".carbon"]),
|
||||
"srcs": attr.label_list(allow_files = [".carbon"]),
|
||||
"_cc_toolchain": attr.label(default = "@bazel_tools//tools/cpp:current_cc_toolchain"),
|
||||
"_cc_toolchain": attr.label(default = "//toolchain/install:carbon_stage1_cc_toolchain"),
|
||||
},
|
||||
executable = True,
|
||||
fragments = ["cpp"],
|
||||
)
|
||||
|
||||
def carbon_binary(name, srcs, deps = [], flags = [], tags = []):
|
||||
@@ -177,7 +199,7 @@ def carbon_binary(name, srcs, deps = [], flags = [], tags = []):
|
||||
# `select` which one we use.
|
||||
internal_exec_toolchain_driver = select({
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": None,
|
||||
"//conditions:default": "//toolchain/install:prefix/bin/carbon",
|
||||
"//conditions:default": "//toolchain/install:carbon-busybox",
|
||||
}),
|
||||
internal_exec_toolchain_data = select({
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": None,
|
||||
@@ -185,10 +207,10 @@ def carbon_binary(name, srcs, deps = [], flags = [], tags = []):
|
||||
}),
|
||||
internal_exec_prebuilt_runtimes = select({
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": None,
|
||||
"//conditions:default": "//toolchain/driver:prebuilt_runtimes",
|
||||
"//conditions:default": "//toolchain/install:built_runtimes",
|
||||
}),
|
||||
internal_target_toolchain_driver = select({
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:prefix/bin/carbon",
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:carbon-busybox",
|
||||
"//conditions:default": None,
|
||||
}),
|
||||
internal_target_toolchain_data = select({
|
||||
@@ -196,7 +218,7 @@ def carbon_binary(name, srcs, deps = [], flags = [], tags = []):
|
||||
"//conditions:default": None,
|
||||
}),
|
||||
internal_target_prebuilt_runtimes = select({
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/driver:prebuilt_runtimes",
|
||||
"//bazel/carbon_rules:use_target_config_carbon_rules_config": "//toolchain/install:built_runtimes",
|
||||
"//conditions:default": None,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -3,9 +3,13 @@
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||
load("@rules_python//python:defs.bzl", "py_library", "py_test")
|
||||
load(":carbon_cc_toolchain_config.bzl", "gen_cc_toolchain_paths_with_stage")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files(["carbon_cc_toolchain_config.bzl"])
|
||||
|
||||
# For use by defs.bzl.
|
||||
# Matches when asan is enabled on a macOS platform.
|
||||
selects.config_setting_group(
|
||||
@@ -61,5 +65,31 @@ filegroup(
|
||||
"cc_toolchain_optimization.bzl",
|
||||
"cc_toolchain_sanitizer_features.bzl",
|
||||
"cc_toolchain_tools.bzl",
|
||||
|
||||
# TODO: Remove this once we can remove the use of it from Carbon
|
||||
# toolchain rules.
|
||||
"cc_toolchain_carbon_project_features.bzl",
|
||||
],
|
||||
)
|
||||
|
||||
gen_cc_toolchain_paths_with_stage(
|
||||
name = "gen_cc_tools_paths",
|
||||
stage = 0,
|
||||
)
|
||||
|
||||
# Test that the default toolchain's Make variables expand correctly.
|
||||
py_test(
|
||||
name = "cc_tools_test",
|
||||
srcs = ["cc_tools_test.py"],
|
||||
args = ["$(location :gen_cc_tools_paths)"],
|
||||
data = [":gen_cc_tools_paths"],
|
||||
deps = [":cc_tools_test_lib"],
|
||||
)
|
||||
|
||||
# Library containing the test logic, used by tests in other packages.
|
||||
py_library(
|
||||
name = "cc_tools_test_lib",
|
||||
srcs = ["cc_tools_test.py"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["@bazel_tools//tools/python/runfiles"],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,547 @@
|
||||
# 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
|
||||
|
||||
"""Starlark cc_toolchain configuration rules for using the Carbon toolchain"""
|
||||
|
||||
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
|
||||
load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
|
||||
load(
|
||||
"@rules_cc//cc:cc_toolchain_config_lib.bzl",
|
||||
"action_config",
|
||||
"flag_group",
|
||||
"flag_set",
|
||||
"tool",
|
||||
)
|
||||
load(
|
||||
"@rules_cc//cc:defs.bzl",
|
||||
"CcToolchainConfigInfo",
|
||||
"cc_toolchain",
|
||||
)
|
||||
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
|
||||
load("//toolchain/runtimes:carbon_runtimes.bzl", "carbon_runtimes_build")
|
||||
load(
|
||||
"carbon_clang_variables.bzl",
|
||||
"clang_include_dirs",
|
||||
"clang_resource_dir",
|
||||
"clang_sysroot",
|
||||
)
|
||||
load(
|
||||
"cc_toolchain_actions.bzl",
|
||||
"all_c_compile_actions",
|
||||
"all_cpp_compile_actions",
|
||||
"all_link_actions",
|
||||
)
|
||||
load("cc_toolchain_carbon_project_features.bzl", "carbon_project_features")
|
||||
load("cc_toolchain_features.bzl", "clang_cc_toolchain_features")
|
||||
load(
|
||||
":cc_toolchain_tools.bzl",
|
||||
"llvm_tool_paths",
|
||||
)
|
||||
|
||||
def _make_action_configs(tools, runtimes_path = None):
|
||||
runtimes_flag = "--no-build-runtimes"
|
||||
if runtimes_path:
|
||||
runtimes_flag = "--prebuilt-runtimes={0}".format(runtimes_path)
|
||||
|
||||
return [
|
||||
action_config(
|
||||
action_name = name,
|
||||
enabled = True,
|
||||
tools = [tools.clang],
|
||||
)
|
||||
for name in all_c_compile_actions
|
||||
] + [
|
||||
action_config(
|
||||
action_name = name,
|
||||
enabled = True,
|
||||
tools = [tools.clangpp],
|
||||
)
|
||||
for name in all_cpp_compile_actions
|
||||
] + [
|
||||
action_config(
|
||||
action_name = name,
|
||||
enabled = True,
|
||||
tools = [tools.carbon_busybox],
|
||||
flag_sets = [flag_set(flag_groups = [flag_group(flags = [
|
||||
runtimes_flag,
|
||||
"link",
|
||||
# We want to allow Bazel to intermingle linked object files and
|
||||
# Clang-spelled link flags. The first `--` starts the list of
|
||||
# initial object files by ending flags to the `link` subcommand,
|
||||
# and the second `--` switches to Clang-spelled flags.
|
||||
"--",
|
||||
"--",
|
||||
])])],
|
||||
)
|
||||
for name in all_link_actions
|
||||
] + [
|
||||
action_config(
|
||||
action_name = name,
|
||||
enabled = True,
|
||||
tools = [tools.llvm_ar],
|
||||
)
|
||||
for name in [ACTION_NAMES.cpp_link_static_library]
|
||||
] + [
|
||||
action_config(
|
||||
action_name = name,
|
||||
enabled = True,
|
||||
tools = [tools.llvm_strip],
|
||||
)
|
||||
for name in [ACTION_NAMES.strip]
|
||||
]
|
||||
|
||||
def _compute_clang_system_include_dirs():
|
||||
system_include_dirs_start_index = None
|
||||
for index, dir in enumerate(clang_include_dirs):
|
||||
# Skip over the include search directories until we find the resource
|
||||
# directory. The system include directories are everything after that.
|
||||
if dir.startswith(clang_resource_dir):
|
||||
system_include_dirs_start_index = index + 1
|
||||
break
|
||||
if not system_include_dirs_start_index:
|
||||
fail("Could not find the resource directory in the clang include " +
|
||||
"directories: {}".format(clang_include_dirs))
|
||||
return clang_include_dirs[system_include_dirs_start_index:]
|
||||
|
||||
def _carbon_cc_toolchain_config_impl(ctx):
|
||||
llvm_bindir = "llvm/bin"
|
||||
clang_bindir = llvm_bindir
|
||||
tools = struct(
|
||||
carbon_busybox = tool(path = "carbon-busybox"),
|
||||
clang = tool(path = clang_bindir + "/clang"),
|
||||
clangpp = tool(path = clang_bindir + "/clang++"),
|
||||
llvm_ar = tool(path = llvm_bindir + "/llvm-ar"),
|
||||
llvm_strip = tool(path = llvm_bindir + "/llvm-strip"),
|
||||
)
|
||||
if ctx.attr.bins:
|
||||
carbon_busybox = None
|
||||
clang = None
|
||||
clangpp = None
|
||||
llvm_ar = None
|
||||
llvm_strip = None
|
||||
for f in ctx.files.bins:
|
||||
if f.basename == "carbon-busybox":
|
||||
carbon_busybox = f
|
||||
elif f.basename == "clang":
|
||||
clang = f
|
||||
elif f.basename == "clang++":
|
||||
clangpp = f
|
||||
elif f.basename == "llvm-ar":
|
||||
llvm_ar = f
|
||||
elif f.basename == "llvm-strip":
|
||||
llvm_strip = f
|
||||
if not all([carbon_busybox, clang, clangpp, llvm_ar, llvm_strip]):
|
||||
fail("Missing required tool in bins: {0}".format(ctx.attr.bins))
|
||||
llvm_bindir = llvm_ar.dirname
|
||||
clang_bindir = clang.dirname
|
||||
tools = struct(
|
||||
carbon_busybox = tool(tool = carbon_busybox),
|
||||
clang = tool(tool = clang),
|
||||
clangpp = tool(tool = clangpp),
|
||||
llvm_ar = tool(tool = llvm_ar),
|
||||
llvm_strip = tool(tool = llvm_strip),
|
||||
)
|
||||
|
||||
# Only use a sysroot if a non-trivial one is set in Carbon's config.
|
||||
builtin_sysroot = None
|
||||
sysroot_include_search = []
|
||||
if clang_sysroot != "None" and clang_sysroot != "/":
|
||||
builtin_sysroot = clang_sysroot
|
||||
sysroot_include_search = ["%sysroot%/usr/include"]
|
||||
|
||||
runtimes_path = None
|
||||
if ctx.attr.runtimes:
|
||||
for f in ctx.files.runtimes:
|
||||
if f.basename == "runtimes_root":
|
||||
runtimes_path = f.dirname
|
||||
break
|
||||
if not runtimes_path:
|
||||
fail("Unable to compute the runtimes path for: {0}".format(
|
||||
ctx.attr.runtimes,
|
||||
))
|
||||
|
||||
identifier = "{0}_toolchain_{1}_{2}".format(
|
||||
ctx.attr.identifier_prefix,
|
||||
ctx.attr.target_cpu,
|
||||
ctx.attr.target_os,
|
||||
)
|
||||
return cc_common.create_cc_toolchain_config_info(
|
||||
ctx = ctx,
|
||||
features = clang_cc_toolchain_features(
|
||||
target_os = ctx.attr.target_os,
|
||||
target_cpu = ctx.attr.target_cpu,
|
||||
|
||||
# TODO: This should be configured externally rather than here so
|
||||
# that the install Carbon toolchain doesn't automatically include
|
||||
# Carbon-project-specific flags. However, that is especially awkward
|
||||
# to do until we fully migrate to a rules-based toolchain, and the
|
||||
# project-specific flags are largely harmless at the moment. We also
|
||||
# omit a meaningful cache key as when using the Carbon toolchain we
|
||||
# don't need it as it is a hermetic part of Bazel.
|
||||
project_features = carbon_project_features(cache_key = ""),
|
||||
),
|
||||
action_configs = _make_action_configs(tools, runtimes_path),
|
||||
cxx_builtin_include_directories = [
|
||||
"runtimes/libunwind/include",
|
||||
"runtimes/libcxx/include",
|
||||
"runtimes/libcxxabi/include",
|
||||
"{}/include".format(clang_resource_dir),
|
||||
"runtimes/clang_resource_dir/include",
|
||||
] + _compute_clang_system_include_dirs() + sysroot_include_search,
|
||||
builtin_sysroot = builtin_sysroot,
|
||||
|
||||
# This configuration only supports local non-cross builds so derive
|
||||
# everything from the target CPU selected.
|
||||
toolchain_identifier = identifier,
|
||||
|
||||
# This is used to expose a "flag" that `config_setting` rules can use to
|
||||
# determine if the compiler is Clang.
|
||||
compiler = "clang",
|
||||
|
||||
# Pass in our tool paths to expose Make variables like $(NM) and
|
||||
# $(OBJCOPY).
|
||||
tool_paths = llvm_tool_paths(llvm_bindir, clang_bindir),
|
||||
)
|
||||
|
||||
carbon_cc_toolchain_config = rule(
|
||||
implementation = _carbon_cc_toolchain_config_impl,
|
||||
attrs = {
|
||||
"bins": attr.label(mandatory = False),
|
||||
"identifier_prefix": attr.string(mandatory = True),
|
||||
"runtimes": attr.label(mandatory = False),
|
||||
"target_cpu": attr.string(mandatory = True),
|
||||
"target_os": attr.string(mandatory = True),
|
||||
},
|
||||
provides = [CcToolchainConfigInfo],
|
||||
)
|
||||
|
||||
def _transition_with_stage_impl(_, attr):
|
||||
return {
|
||||
"//:bootstrap_stage": attr.stage,
|
||||
"//:runtimes_build": attr.enable_runtimes_build,
|
||||
}
|
||||
|
||||
_transition_with_stage = transition(
|
||||
inputs = [],
|
||||
outputs = [
|
||||
"//:bootstrap_stage",
|
||||
"//:runtimes_build",
|
||||
],
|
||||
implementation = _transition_with_stage_impl,
|
||||
)
|
||||
|
||||
def _filegroup_with_stage_impl(ctx):
|
||||
return [DefaultInfo(files = depset(ctx.files.srcs))]
|
||||
|
||||
filegroup_with_stage = rule(
|
||||
implementation = _filegroup_with_stage_impl,
|
||||
attrs = {
|
||||
# Whether to enable runtimes building for the sources of this filegroup.
|
||||
"enable_runtimes_build": attr.bool(default = False),
|
||||
|
||||
# Mark that our dependencies are built through a transition.
|
||||
"srcs": attr.label_list(mandatory = True, cfg = _transition_with_stage),
|
||||
|
||||
# The bootstrap stage that the sources of this filegroup should be built
|
||||
# with.
|
||||
"stage": attr.int(mandatory = True),
|
||||
|
||||
# Enable transitions in this rule.
|
||||
"_allowlist_function_transition": attr.label(
|
||||
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
|
||||
),
|
||||
},
|
||||
doc = """
|
||||
A filegroup whose sources are built using a specific toolchain stage, and
|
||||
which provides an interface to build those sources with or without enabling
|
||||
runtimes building.
|
||||
""",
|
||||
)
|
||||
|
||||
def _exec_filegroup_impl(ctx):
|
||||
return [DefaultInfo(files = depset(ctx.files.srcs))]
|
||||
|
||||
_exec_filegroup = rule(
|
||||
implementation = _exec_filegroup_impl,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(cfg = "exec"),
|
||||
},
|
||||
)
|
||||
|
||||
def filegroup_with_stage_and_exec(name, srcs, stage, tags = []):
|
||||
"""Wraps `filegroup_with_stage` with a conditional `exec` config transition.
|
||||
|
||||
When `//:bootstrap_exec_config` is disabled, this works exactly like
|
||||
`filegroup_with_stage`. But when it is _enabled_, it also adds an `exec`
|
||||
config transition. This allows bootstrapping for a target that is not exec
|
||||
compatible with the host, and in general makes bootstrapping more robust at
|
||||
the expense of a likely duplicate build of the entire toolchain.
|
||||
"""
|
||||
filegroup_with_stage(
|
||||
name = name + "_stage_only",
|
||||
srcs = srcs,
|
||||
stage = stage,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
_exec_filegroup(
|
||||
name = name + "_with_exec",
|
||||
srcs = [":" + name + "_stage_only"],
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
native.alias(
|
||||
name = name,
|
||||
actual = select({
|
||||
"//:bootstrap_with_exec_config": ":" + name + "_with_exec",
|
||||
"//conditions:default": ":" + name + "_stage_only",
|
||||
}),
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
def _gen_cc_toolchain_paths_impl(ctx):
|
||||
cc_toolchain = find_cpp_toolchain(ctx)
|
||||
|
||||
expanded_vars = [
|
||||
ctx.expand_make_variables("vars", v, {})
|
||||
for v in ctx.attr.vars
|
||||
]
|
||||
|
||||
out = ctx.actions.declare_file(ctx.attr.name + ".txt")
|
||||
ctx.actions.write(out, "\n".join(expanded_vars) + "\n")
|
||||
|
||||
# Include all toolchain files in runfiles.
|
||||
runfiles = ctx.runfiles(files = [out]).merge(
|
||||
ctx.runfiles(transitive_files = cc_toolchain.all_files),
|
||||
)
|
||||
|
||||
return [DefaultInfo(files = depset([out]), runfiles = runfiles)]
|
||||
|
||||
gen_cc_toolchain_paths_with_stage = rule(
|
||||
implementation = _gen_cc_toolchain_paths_impl,
|
||||
attrs = {
|
||||
"enable_runtimes_build": attr.bool(default = False),
|
||||
"stage": attr.int(mandatory = True),
|
||||
"vars": attr.string_list(
|
||||
default = ["$(CC)", "$(AR)", "$(NM)", "$(OBJCOPY)", "$(STRIP)"],
|
||||
),
|
||||
"_allowlist_function_transition": attr.label(
|
||||
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
|
||||
),
|
||||
"_cc_toolchain": attr.label(
|
||||
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
|
||||
),
|
||||
},
|
||||
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
|
||||
cfg = _transition_with_stage,
|
||||
)
|
||||
|
||||
def carbon_cc_toolchain_suite(
|
||||
name,
|
||||
all_hdrs,
|
||||
base_files,
|
||||
clang_hdrs,
|
||||
platforms,
|
||||
runtimes_cfg,
|
||||
build_stage = 1,
|
||||
base_stage = 0,
|
||||
tags = []):
|
||||
"""Create a Carbon `cc_toolchain` for the current target platform.
|
||||
|
||||
This provides the final toolchain for Carbon, but also all of the
|
||||
infrastructure for supporting on-demand built runtimes in this toolchain.
|
||||
|
||||
There is also support for bootstrapping, where one `build_stage` toolchain
|
||||
builds on top of another `base_stage`.
|
||||
|
||||
Args:
|
||||
name:
|
||||
The name of the toolchain suite to produce, used as the base of the
|
||||
names of each component of the toolchain suite.
|
||||
all_hdrs: A list of header files to include in the toolchain.
|
||||
base_files: A list of files to include in the toolchain.
|
||||
build_stage: The stage to use for the build files.
|
||||
base_stage: The stage to use for the base files.
|
||||
clang_hdrs: A list of header files to include in the toolchain.
|
||||
platforms: An array of (os, cpu) pairs to support in the toolchain.
|
||||
runtimes_cfg: The runtimes configuration to use in the toolchain.
|
||||
tags: Tags to apply to the toolchain.
|
||||
"""
|
||||
|
||||
# First, declare file groups that are explicitly built using the base stage,
|
||||
# and not in the runtimes build. These allow us to form the inputs to both
|
||||
# the runtimes toolchain and the main toolchain of this stage that are built
|
||||
# entirely by the base stage toolchain.
|
||||
filegroup_with_stage_and_exec(
|
||||
name = "{}_clang_hdrs".format(name),
|
||||
srcs = clang_hdrs,
|
||||
stage = base_stage,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
filegroup_with_stage_and_exec(
|
||||
name = "{}_base_files".format(name),
|
||||
srcs = base_files,
|
||||
stage = base_stage,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
filegroup_with_stage_and_exec(
|
||||
name = "{}_runtimes_compile_files".format(name),
|
||||
srcs = [
|
||||
":{}_base_files".format(name),
|
||||
":{}_clang_hdrs".format(name),
|
||||
],
|
||||
stage = base_stage,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
filegroup_with_stage_and_exec(
|
||||
name = "{}_compile_files".format(name),
|
||||
srcs = [":{}_base_files".format(name)] + all_hdrs,
|
||||
stage = base_stage,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
# Now build a configuration and toolchain that is configured to work
|
||||
# _without_ runtimes, and be used to _build_ the runtimes on-demand.
|
||||
carbon_cc_toolchain_config(
|
||||
name = "{}_runtimes_toolchain_config".format(name),
|
||||
identifier_prefix = "{}_runtimes".format(name),
|
||||
target_cpu = select({
|
||||
# Note that we need to select on both OS and CPU so that we end up
|
||||
# spelling the CPU in the correct OS-specific ways.
|
||||
":is_{}_{}".format(os, cpu): cpu
|
||||
for os, cpus in platforms.items()
|
||||
for cpu in cpus
|
||||
}),
|
||||
target_os = select({
|
||||
"@platforms//os:{}".format(os): os
|
||||
for os in platforms.keys()
|
||||
}),
|
||||
bins = ":{}_base_files".format(name),
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
cc_toolchain(
|
||||
name = "{}_runtimes_cc_toolchain".format(name),
|
||||
all_files = ":{}_runtimes_compile_files".format(name),
|
||||
ar_files = ":{}_base_files".format(name),
|
||||
as_files = ":{}_runtimes_compile_files".format(name),
|
||||
compiler_files = ":{}_runtimes_compile_files".format(name),
|
||||
dwp_files = ":{}_base_files".format(name),
|
||||
linker_files = ":{}_base_files".format(name),
|
||||
objcopy_files = ":{}_base_files".format(name),
|
||||
strip_files = ":{}_base_files".format(name),
|
||||
toolchain_config = ":{}_runtimes_toolchain_config".format(name),
|
||||
toolchain_identifier = select({
|
||||
":is_{}_{}".format(os, cpu): "{}_{}_{}_runtimes_toolchain".format(name, os, cpu)
|
||||
for os, cpus in platforms.items()
|
||||
for cpu in cpus
|
||||
}),
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
native.toolchain(
|
||||
name = "{}_runtimes_toolchain".format(name),
|
||||
target_settings = [
|
||||
":is_bootstrap_stage_{}".format(build_stage),
|
||||
":is_runtimes_build",
|
||||
],
|
||||
use_target_platform_constraints = True,
|
||||
toolchain = ":{}_runtimes_cc_toolchain".format(name),
|
||||
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
# Now that we have a toolchain for building runtimes, actually do the build
|
||||
# here using the runtimes config provided to us. This is important to do
|
||||
# here because we need each runtimes build for a particular bootstrapping
|
||||
# stage of the toolchain to be distinct.
|
||||
carbon_runtimes_build(
|
||||
name = "{}_runtimes_build".format(name),
|
||||
config = runtimes_cfg,
|
||||
clang_hdrs = [":{}_clang_hdrs".format(name)],
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
# Wrap the built runtimes for this stage in a filegroup that ensures they
|
||||
# are built at this stage, but with the runtimes build enabled. This will
|
||||
# select the runtimes build toolchain above that doesn't yet provide any
|
||||
# runtimes, avoiding a cycle when the main toolchain below depends on these
|
||||
# runtimes.
|
||||
filegroup_with_stage(
|
||||
name = "{}_runtimes".format(name),
|
||||
enable_runtimes_build = True,
|
||||
srcs = ["{}_runtimes_build".format(name)],
|
||||
stage = build_stage,
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
# Now we can build the main toolchain configuration, filegroups including
|
||||
# the on-demand built runtimes, and the final tolochain itself.
|
||||
carbon_cc_toolchain_config(
|
||||
name = "{}_toolchain_config".format(name),
|
||||
identifier_prefix = name,
|
||||
target_cpu = select({
|
||||
# Note that we need to select on both OS and CPU so that we end up
|
||||
# spelling the CPU in the correct OS-specific ways.
|
||||
":is_{}_{}".format(os, cpu): cpu
|
||||
for os, cpus in platforms.items()
|
||||
for cpu in cpus
|
||||
}),
|
||||
target_os = select({
|
||||
"@platforms//os:{}".format(os): os
|
||||
for os in platforms.keys()
|
||||
}),
|
||||
runtimes = ":{}_runtimes".format(name),
|
||||
bins = ":{}_base_files".format(name),
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
native.filegroup(
|
||||
name = "{}_linker_files".format(name),
|
||||
srcs = [
|
||||
":{}_base_files".format(name),
|
||||
":{}_runtimes".format(name),
|
||||
],
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
native.filegroup(
|
||||
name = "{}_all_files".format(name),
|
||||
srcs = [
|
||||
":{}_compile_files".format(name),
|
||||
":{}_linker_files".format(name),
|
||||
],
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
cc_toolchain(
|
||||
name = "{}_cc_toolchain".format(name),
|
||||
all_files = ":{}_all_files".format(name),
|
||||
ar_files = ":" + name + "_base_files",
|
||||
as_files = ":" + name + "_compile_files",
|
||||
compiler_files = ":" + name + "_compile_files",
|
||||
dwp_files = ":" + name + "_linker_files",
|
||||
linker_files = ":" + name + "_linker_files",
|
||||
objcopy_files = ":" + name + "_base_files",
|
||||
strip_files = ":" + name + "_base_files",
|
||||
toolchain_config = ":" + name + "_toolchain_config",
|
||||
toolchain_identifier = select({
|
||||
":is_{}_{}".format(os, cpu): "{}_{}_{}_toolchain".format(name, os, cpu)
|
||||
for os, cpus in platforms.items()
|
||||
for cpu in cpus
|
||||
}),
|
||||
tags = tags,
|
||||
)
|
||||
|
||||
native.toolchain(
|
||||
name = name + "_toolchain",
|
||||
target_settings = [":is_bootstrap_stage_{}".format(build_stage), ":not_runtimes_build"],
|
||||
use_target_platform_constraints = True,
|
||||
toolchain = ":" + name + "_cc_toolchain",
|
||||
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
|
||||
tags = tags,
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
# 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
|
||||
|
||||
"""A Starlark file exporting detected Carbon toolchain configuration variables.
|
||||
|
||||
This file gets processed by a repository rule, substituting the `VARIABLE`s with
|
||||
values, for example using an invocation of `carbon config`.
|
||||
"""
|
||||
|
||||
load(
|
||||
"@bazel_cc_toolchain//:clang_detected_variables.bzl",
|
||||
_clang_include_dirs = "clang_include_dirs",
|
||||
_clang_resource_dir = "clang_resource_dir",
|
||||
_sysroot_dir = "sysroot_dir",
|
||||
)
|
||||
|
||||
clang_include_dirs = _clang_include_dirs
|
||||
clang_resource_dir = _clang_resource_dir
|
||||
clang_sysroot = _sysroot_dir
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
"""Definitions used for the base features of a `cc_toolchain_config`."""
|
||||
|
||||
load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES")
|
||||
load(
|
||||
"@rules_cc//cc:cc_toolchain_config_lib.bzl",
|
||||
"feature",
|
||||
@@ -92,12 +93,41 @@ output_flags_feature = feature(
|
||||
],
|
||||
)
|
||||
|
||||
strip_feature = feature(
|
||||
name = "strip_flags",
|
||||
enabled = True,
|
||||
flag_sets = [flag_set(
|
||||
actions = [ACTION_NAMES.strip],
|
||||
flag_groups = [
|
||||
flag_group(
|
||||
flags = ["-S"],
|
||||
),
|
||||
flag_group(
|
||||
flags = ["-p"],
|
||||
),
|
||||
flag_group(
|
||||
expand_if_available = "output_file",
|
||||
flags = ["-o", "%{output_file}"],
|
||||
),
|
||||
flag_group(
|
||||
iterate_over = "stripopts",
|
||||
flags = ["%{stripopts}"],
|
||||
),
|
||||
flag_group(
|
||||
expand_if_available = "input_file",
|
||||
flags = ["%{input_file}"],
|
||||
),
|
||||
],
|
||||
)],
|
||||
)
|
||||
|
||||
base_features = [
|
||||
dbg_feature,
|
||||
fastbuild_feature,
|
||||
host_feature,
|
||||
no_legacy_features_feature,
|
||||
opt_feature,
|
||||
strip_feature,
|
||||
supports_pic_feature,
|
||||
supports_dynamic_linker_feature,
|
||||
supports_start_end_lib_feature,
|
||||
|
||||
@@ -27,10 +27,9 @@ carbon_project_fastbuild_feature = feature(
|
||||
enabled = True,
|
||||
requires = [feature_set(["fastbuild"])],
|
||||
implies = [
|
||||
"asan",
|
||||
"asan_min_size",
|
||||
"minimal_optimization_flags",
|
||||
"minimal_debug_info_flags",
|
||||
"preserve_call_stacks",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ minimal_optimization_flags = feature(
|
||||
name = "minimal_optimization_flags",
|
||||
flag_sets = [flag_set(
|
||||
actions = codegen_compile_actions,
|
||||
flag_groups = [flag_group(flags = ["-O1"])],
|
||||
flag_groups = [flag_group(flags = ["-Og"])],
|
||||
)],
|
||||
)
|
||||
default_optimization_flags = feature(
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Tests that the C++ toolchain tools can be executed.
|
||||
|
||||
This script reads a file containing paths to C++ tools (like clang++, llvm-ar)
|
||||
and attempts to run each with `--version` to verify they are functional.
|
||||
"""
|
||||
|
||||
__copyright__ = """
|
||||
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
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from bazel_tools.tools.python.runfiles import runfiles
|
||||
|
||||
|
||||
def test_tools() -> None:
|
||||
"""Reads paths from file and runs each tool with --version."""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: cc_tools_test.py <paths_file>")
|
||||
sys.exit(1)
|
||||
|
||||
paths_file = sys.argv[1]
|
||||
print(f"Reading tools from: {paths_file}")
|
||||
with open(paths_file, "r") as f:
|
||||
tools = [line.strip() for line in f if line.strip()]
|
||||
|
||||
print(f"Testing tools: {tools}")
|
||||
r = runfiles.Create()
|
||||
repo_name = os.environ.get("TEST_WORKSPACE") or "_main"
|
||||
|
||||
for tool in tools:
|
||||
if "bazel-out/" in tool:
|
||||
_, _, rest = tool.partition("bazel-out/")
|
||||
_, sep, after = rest.partition("bin/")
|
||||
if sep:
|
||||
tool = after
|
||||
|
||||
rlocation_path = os.path.join(repo_name, tool)
|
||||
tool = r.Rlocation(rlocation_path)
|
||||
|
||||
print(f"Running {tool} --version")
|
||||
try:
|
||||
res = subprocess.run(
|
||||
[tool, "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
print(res.stdout)
|
||||
except Exception as e:
|
||||
print(f"Failed to run {tool}: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_tools()
|
||||
@@ -17,7 +17,7 @@ load(
|
||||
load(
|
||||
":clang_detected_variables.bzl",
|
||||
"clang_bindir",
|
||||
"clang_include_dirs_list",
|
||||
"clang_include_dirs",
|
||||
"clang_resource_dir",
|
||||
"clang_version_for_cache",
|
||||
"llvm_bindir",
|
||||
@@ -40,7 +40,7 @@ def _impl(ctx):
|
||||
extra_cpp_features = [libcxx_feature(llvm_bindir, clang_bindir)],
|
||||
),
|
||||
action_configs = llvm_action_configs(llvm_bindir, clang_bindir),
|
||||
cxx_builtin_include_directories = clang_include_dirs_list + [
|
||||
cxx_builtin_include_directories = clang_include_dirs + [
|
||||
# Add Clang's resource directory to the end of the builtin include
|
||||
# directories to cover the use of sanitizer resource files by the
|
||||
# driver.
|
||||
@@ -92,7 +92,7 @@ def cc_local_toolchain_suite(name, configs):
|
||||
target_cpu = cpu,
|
||||
)
|
||||
cc_toolchain(
|
||||
name = config_name + "_tools",
|
||||
name = config_name + "_toolchain",
|
||||
all_files = ":" + name + "_empty",
|
||||
ar_files = ":" + name + "_empty",
|
||||
as_files = ":" + name + "_empty",
|
||||
@@ -109,7 +109,11 @@ def cc_local_toolchain_suite(name, configs):
|
||||
native.toolchain(
|
||||
name = config_name,
|
||||
exec_compatible_with = compatible_with,
|
||||
target_settings = [
|
||||
"@carbon//toolchain/install:is_bootstrap_stage_0",
|
||||
"@carbon//toolchain/install:not_runtimes_build",
|
||||
],
|
||||
target_compatible_with = compatible_with,
|
||||
toolchain = config_name + "_tools",
|
||||
toolchain = config_name + "_toolchain",
|
||||
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
|
||||
)
|
||||
|
||||
@@ -226,7 +226,7 @@ def _configure_clang_toolchain_impl(repository_ctx):
|
||||
repository_ctx.attr._clang_detected_variables_template,
|
||||
substitutions = {
|
||||
"{CLANG_BINDIR}": str(clang.dirname),
|
||||
"{CLANG_INCLUDE_DIRS_LIST}": str(
|
||||
"{CLANG_INCLUDE_DIRS}": str(
|
||||
[str(path) for path in include_dirs],
|
||||
),
|
||||
"{CLANG_RESOURCE_DIR}": resource_dir,
|
||||
|
||||
@@ -14,5 +14,5 @@ clang_bindir = "{CLANG_BINDIR}"
|
||||
clang_version = {CLANG_VERSION}
|
||||
clang_version_for_cache = "{CLANG_VERSION_FOR_CACHE}"
|
||||
clang_resource_dir = "{CLANG_RESOURCE_DIR}"
|
||||
clang_include_dirs_list = {CLANG_INCLUDE_DIRS_LIST}
|
||||
clang_include_dirs = {CLANG_INCLUDE_DIRS}
|
||||
sysroot_dir = "{SYSROOT}"
|
||||
|
||||
@@ -13,8 +13,8 @@ load("@rules_python//python:defs.bzl", "py_test")
|
||||
filegroup(
|
||||
name = "non_test_cc_rules",
|
||||
data = [
|
||||
"//toolchain/install:carbon_toolchain_tar_gz_rule",
|
||||
"//toolchain/install:carbon_toolchain_tar_rule",
|
||||
"//toolchain/install:carbon_toolchain_tar",
|
||||
"//toolchain/install:carbon_toolchain_tar_gz",
|
||||
],
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
+12
-10
@@ -1,7 +1,7 @@
|
||||
Commit ID: 959284a466ca392a5ccd501cf1c1622d1f423c06
|
||||
Commit ID: d3b82534c2546a892a27856672ed95a7db97dba3
|
||||
Change ID: zyxuvzwmzsnorloyuupuurxkppkoplnw
|
||||
Author : Chandler Carruth <chandlerc@gmail.com> (2026-02-16 23:17:06)
|
||||
Committer: Chandler Carruth <chandlerc@gmail.com> (2026-03-08 07:43:17)
|
||||
Committer: Chandler Carruth <chandlerc@gmail.com> (2026-03-11 07:54:02)
|
||||
|
||||
Improve compiler-rt build structure and export compilation info
|
||||
|
||||
@@ -12,7 +12,7 @@ Committer: Chandler Carruth <chandlerc@gmail.com> (2026-03-08 07:43:17)
|
||||
compiling these source files.
|
||||
|
||||
diff --git a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel
|
||||
index 4ded226174..0c3a7daaee 100644
|
||||
index 4ded226174..3b5b8fc787 100644
|
||||
--- a/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel
|
||||
+++ b/utils/bazel/llvm-project-overlay/compiler-rt/BUILD.bazel
|
||||
@@ -3,6 +3,7 @@
|
||||
@@ -98,12 +98,14 @@ index 4ded226174..0c3a7daaee 100644
|
||||
] + [
|
||||
AARCH64_OUTLINE_ATOMICS_FMT.format(pat, size, model)
|
||||
for (pat, size, model) in AARCH64_OUTLINE_ATOMICS
|
||||
@@ -328,10 +360,18 @@
|
||||
@@ -328,10 +360,20 @@
|
||||
"lib/builtins/aarch64/lse.S",
|
||||
# These files are provided by SME-specific file groups above.
|
||||
"lib/builtins/aarch64/*sme*",
|
||||
+ # This is only used with MinGW.
|
||||
+ "lib/builtins/aarch64/chkstk.S",
|
||||
+ # TODO: Remove this once we have a way of accessing `SipHash.h`.
|
||||
+ "lib/builtins/aarch64/emupac.cpp",
|
||||
],
|
||||
),
|
||||
)
|
||||
@@ -117,7 +119,7 @@ index 4ded226174..0c3a7daaee 100644
|
||||
BUILTINS_ARM_VFP_SRCS_PATTERNS = [
|
||||
"lib/builtins/arm/*vfp*.S",
|
||||
"lib/builtins/arm/*vfp*.c",
|
||||
@@ -348,9 +388,19 @@
|
||||
@@ -348,9 +390,19 @@
|
||||
),
|
||||
)
|
||||
|
||||
@@ -138,7 +140,7 @@ index 4ded226174..0c3a7daaee 100644
|
||||
srcs = glob(
|
||||
[
|
||||
"lib/builtins/arm/*.S",
|
||||
@@ -359,14 +409,52 @@
|
||||
@@ -359,14 +411,52 @@
|
||||
"lib/builtins/arm/*.h",
|
||||
],
|
||||
allow_empty = True,
|
||||
@@ -196,7 +198,7 @@ index 4ded226174..0c3a7daaee 100644
|
||||
[
|
||||
"lib/builtins/ppc/*.S",
|
||||
"lib/builtins/ppc/*.c",
|
||||
@@ -377,17 +465,64 @@
|
||||
@@ -377,17 +467,64 @@
|
||||
),
|
||||
)
|
||||
|
||||
@@ -272,7 +274,7 @@ index 4ded226174..0c3a7daaee 100644
|
||||
)
|
||||
|
||||
# Source files for the x86 architecture specific builtins (both 32-bit and
|
||||
@@ -402,8 +537,14 @@
|
||||
@@ -402,8 +539,14 @@
|
||||
|
||||
# Source files for the x86-64 architecture specific builtins.
|
||||
filegroup(
|
||||
@@ -289,7 +291,7 @@ index 4ded226174..0c3a7daaee 100644
|
||||
[
|
||||
"lib/builtins/x86_64/*.S",
|
||||
"lib/builtins/x86_64/*.c",
|
||||
@@ -411,13 +552,29 @@
|
||||
@@ -411,13 +554,29 @@
|
||||
"lib/builtins/x86_64/*.h",
|
||||
],
|
||||
allow_empty = True,
|
||||
@@ -321,7 +323,7 @@ index 4ded226174..0c3a7daaee 100644
|
||||
[
|
||||
"lib/builtins/i386/*.S",
|
||||
"lib/builtins/i386/*.c",
|
||||
@@ -429,28 +586,16 @@
|
||||
@@ -429,28 +588,16 @@
|
||||
# This file is used for both i386 and x86_64 and so included in the
|
||||
# broader x86 sources.
|
||||
"lib/builtins/i386/fp_mode.c",
|
||||
|
||||
@@ -8,10 +8,6 @@ def _get_files(ctx):
|
||||
files = []
|
||||
for src in ctx.attr.srcs:
|
||||
files.extend([f.path for f in src[DefaultInfo].files.to_list()])
|
||||
files.extend([
|
||||
f.path
|
||||
for f in src[DefaultInfo].default_runfiles.files.to_list()
|
||||
])
|
||||
|
||||
if ctx.attr.strip_package_dir:
|
||||
# Files may or may not be prefixed with the bin directory, and then
|
||||
|
||||
@@ -643,7 +643,7 @@ auto DirRef::ReadlinkSlow(const std::filesystem::path& path)
|
||||
if (buffer_size == 0) {
|
||||
buffer_size = MinBufferSize;
|
||||
}
|
||||
large_buffer.resize(status.size());
|
||||
large_buffer.resize(buffer_size);
|
||||
ssize_t result =
|
||||
readlinkat(dfd_, path.c_str(), large_buffer.data(), large_buffer.size());
|
||||
if (result == -1) {
|
||||
|
||||
@@ -18,17 +18,18 @@ namespace Carbon {
|
||||
//
|
||||
// This provides a hashtable-specific extension point to implement equality
|
||||
// comparison within a hashtable key context. By default, it will use
|
||||
// `operator==` on the LHS and RHS operands. However, types can provide a
|
||||
// dedicated customization point by implementing a free function that can be
|
||||
// found by ADL for your type called `CarbonHashtableEq` with the following
|
||||
// signature:
|
||||
// `operator==` on the LHS and RHS operands if they are of the identical type.
|
||||
// However, types can provide a dedicated customization point by implementing a
|
||||
// free function that can be found by ADL for your type called
|
||||
// `CarbonHashtableEq` with the following signature:
|
||||
//
|
||||
// ```cpp
|
||||
// auto CarbonHashtableEq(const YourType& lhs, const YourType& rhs) -> bool;
|
||||
// ```
|
||||
//
|
||||
// Any such overload will be able to override the default we provide for types
|
||||
// that can compare with `==`.
|
||||
// that can compare with `==`. This overload may only compare two objects equal
|
||||
// if the hash of those two objects are identical.
|
||||
//
|
||||
// This library also provides any customization points for LLVM or standard
|
||||
// library types either lacking `operator==` or where that operator is not
|
||||
@@ -161,8 +162,37 @@ inline auto CarbonHashtableEq(const llvm::APFloat& lhs,
|
||||
return lhs.bitwiseIsEqual(rhs);
|
||||
}
|
||||
|
||||
template <typename LeftT, typename RightT>
|
||||
inline auto CarbonHashtableEq(const LeftT& lhs, const RightT& rhs) -> bool
|
||||
inline auto CarbonHashtableEq(llvm::StringRef lhs, const std::string& rhs)
|
||||
-> bool {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline auto CarbonHashtableEq(llvm::MutableArrayRef<T> lhs,
|
||||
llvm::ArrayRef<T> rhs) -> bool {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
template <typename LHS, typename RHS>
|
||||
inline auto CarbonHashtableEq(const LHS& lhs, const RHS& rhs) -> bool
|
||||
requires(requires {
|
||||
{ CarbonHashtableEq(rhs, lhs) } -> std::convertible_to<bool>;
|
||||
})
|
||||
{
|
||||
return CarbonHashtableEq(rhs, lhs);
|
||||
}
|
||||
|
||||
// Provides symmetric equality so the `CarbonHashtableEq` operands aren't
|
||||
// ordered.
|
||||
//
|
||||
// If this template proves problematic in any way, we can revisit it - the
|
||||
// `CarbonHashtableEq` functions don't really need to be symmetric, since they
|
||||
// generally represent an implicit conversion which is often only one-way (eg:
|
||||
// MutableArrayRef converts to ArrayRef, but not the other way around) - but
|
||||
// documenting/describing that asymmetry felt a little awkward too - so maybe
|
||||
// this template is an OK solution for now.
|
||||
template <typename T>
|
||||
inline auto CarbonHashtableEq(const T& lhs, const T& rhs) -> bool
|
||||
requires(requires {
|
||||
{ lhs == rhs } -> std::convertible_to<bool>;
|
||||
})
|
||||
|
||||
@@ -45,6 +45,10 @@ struct TestData : Printable<TestData> {
|
||||
|
||||
static_assert(std::is_copy_constructible_v<TestData>);
|
||||
|
||||
inline auto CarbonHashtableEq(int lhs, TestData rhs) -> bool {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
// Non-trivial type for testing.
|
||||
struct MoveOnlyTestData : Printable<TestData> {
|
||||
int value;
|
||||
@@ -85,6 +89,10 @@ struct MoveOnlyTestData : Printable<TestData> {
|
||||
static_assert(!std::is_copy_constructible_v<MoveOnlyTestData>);
|
||||
static_assert(std::is_move_constructible_v<MoveOnlyTestData>);
|
||||
|
||||
inline auto CarbonHashtableEq(int lhs, const MoveOnlyTestData& rhs) -> bool {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
// Test stateless key context that produces different hashes from normal.
|
||||
// Changing the hash values should result in test failures if the context ever
|
||||
// fails to be used.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package Core library "prelude";
|
||||
|
||||
export import library "prelude/copy";
|
||||
export import library "prelude/default";
|
||||
export import library "prelude/destroy";
|
||||
export import library "prelude/iterate";
|
||||
export import library "prelude/operators";
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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
|
||||
|
||||
package Core library "prelude/default";
|
||||
|
||||
import library "prelude/types/bool";
|
||||
import library "prelude/types/int_literal";
|
||||
|
||||
// Provides the default value of an object. If implemented for a type `T`, this
|
||||
// is used to initialize declarations without an explicit initializer, such as
|
||||
// `var x: T;`, and leaves them in a fully-formed state.
|
||||
interface Default { fn Op() -> Self; }
|
||||
|
||||
// Indicates that a type permits unformed initialization, which leaves the
|
||||
// object in a state where calling the destructor is valid but optional, and no
|
||||
// other operations on the object except for reinitialization are permitted.
|
||||
interface UnformedInit {
|
||||
// TODO: This should probably be:
|
||||
// let StructT:! type;
|
||||
// fn Op() -> StructT;
|
||||
// and should be able to initialize a subset of the fields. For now we always
|
||||
// leave the object uninitialized when it is in an unformed state.
|
||||
// See https://github.com/carbon-language/carbon-lang/pull/5913
|
||||
}
|
||||
|
||||
// Implementations for some builtin types. These need to be here to satisfy the
|
||||
// orphan rule because these builtin types have no associated library of their
|
||||
// own.
|
||||
impl bool as UnformedInit {}
|
||||
impl forall [T:! type] T* as UnformedInit {}
|
||||
impl forall [T:! UnformedInit, N:! IntLiteral()] array(T, N) as UnformedInit {}
|
||||
// TODO: Generalize these to apply to tuples and structs containing only
|
||||
// `UnformedInit` types.
|
||||
impl () as UnformedInit {}
|
||||
impl {} as UnformedInit {}
|
||||
|
||||
// Provides a default, possibly unformed, value of an object. This should not be
|
||||
// implemented directly. Instead, implement `Default` to provide a fully-formed
|
||||
// state or (eventually) `UnformedInit` to provide an unformed state.
|
||||
interface DefaultOrUnformed {
|
||||
// TODO: This should return `MaybeUnformed(Self)` once that is supported.
|
||||
fn Op() -> Self;
|
||||
}
|
||||
|
||||
final impl forall [T:! Default] T as DefaultOrUnformed {
|
||||
fn Op() -> Self {
|
||||
return T.(Default.Op)();
|
||||
}
|
||||
}
|
||||
|
||||
impl forall [T:! UnformedInit] T as DefaultOrUnformed {
|
||||
fn Op() -> Self = "make_uninitialized";
|
||||
}
|
||||
@@ -9,3 +9,4 @@ export import library "prelude/operators/as";
|
||||
export import library "prelude/operators/bitwise";
|
||||
export import library "prelude/operators/comparison";
|
||||
export import library "prelude/operators/index";
|
||||
export import library "prelude/operators/deref";
|
||||
|
||||
@@ -44,6 +44,17 @@ impl forall [T:! type, U:! ImplicitAs(T)] const U as ImplicitAs(T) {
|
||||
fn Convert[self: const U]() -> T { return (self as U).Convert(); }
|
||||
}
|
||||
|
||||
// `const` can be added to a pointer.
|
||||
// TODO: This is also provided as a builtin conversion. We provide it here so
|
||||
// that Optional(T*) can implicitly convert to Optional(const T*). See #5750.
|
||||
impl forall [T:! type] T* as ImplicitAs(const T*) {
|
||||
fn Convert[self: T*]() -> const T* = "pointer.unsafe_convert";
|
||||
}
|
||||
|
||||
impl forall [T:! type] T* as As(const T*) {
|
||||
fn Convert[self: T*]() -> const T* = "pointer.unsafe_convert";
|
||||
}
|
||||
|
||||
// Pointer types can be unsafely cast to other pointer types.
|
||||
// TODO: Should `unsafe as` be able to remove `const`?
|
||||
impl forall [T:! type, U:! type] T* as UnsafeAs(U*) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// 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
|
||||
|
||||
package Core library "prelude/operators/deref";
|
||||
|
||||
// TODO: Align with https://docs.carbon-lang.dev/docs/design/values.html#dereferencing-customization.
|
||||
interface CppUnsafeDeref {
|
||||
let Result:! type;
|
||||
fn Op[ref self: Self]() -> ref Result;
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/char";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/operators";
|
||||
import library "prelude/types/uint";
|
||||
@@ -17,6 +18,8 @@ class Char {
|
||||
adapt u8;
|
||||
}
|
||||
|
||||
impl Char as UnformedInit {}
|
||||
|
||||
impl Char as Copy {
|
||||
fn Op[self: Self]() -> Self = "primitive_copy";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/cpp/int";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/operators";
|
||||
import library "prelude/types/int";
|
||||
import library "prelude/types/int_literal";
|
||||
@@ -28,6 +29,11 @@ class CppCompat.ULongLong64 {
|
||||
adapt u64;
|
||||
}
|
||||
|
||||
impl CppCompat.Long32 as UnformedInit {}
|
||||
impl CppCompat.ULong32 as UnformedInit {}
|
||||
impl CppCompat.LongLong64 as UnformedInit {}
|
||||
impl CppCompat.ULongLong64 as UnformedInit {}
|
||||
|
||||
// Copy
|
||||
|
||||
impl CppCompat.Long32 as Copy {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/cpp/nullptr";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/operators/as";
|
||||
import library "prelude/types/cpp/void";
|
||||
@@ -29,9 +30,13 @@ class CppCompat.NullptrT {
|
||||
// TODO: Give this type a custom empty value representation.
|
||||
adapt MaybeUnformed(VoidBase*);
|
||||
|
||||
// TODO: This should be just
|
||||
// fn Make() -> Self = "make_uninitialized";
|
||||
// but we don't yet delay processing builtin function definitions until the
|
||||
// end of the enclosing class.
|
||||
fn Make() -> Self {
|
||||
returned var s: Self;
|
||||
return var;
|
||||
fn MakeImpl() -> Self = "make_uninitialized";
|
||||
return MakeImpl();
|
||||
}
|
||||
|
||||
impl as Copy {
|
||||
@@ -48,3 +53,5 @@ class CppCompat.NullptrT {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CppCompat.NullptrT as UnformedInit {}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/float";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/operators";
|
||||
import library "prelude/types/float_literal";
|
||||
@@ -16,6 +17,8 @@ class Float(N:! IntLiteral()) {
|
||||
adapt MakeFloat(N);
|
||||
}
|
||||
|
||||
impl forall [N:! IntLiteral()] Float(N) as UnformedInit {}
|
||||
|
||||
// Copy.
|
||||
|
||||
impl forall [N:! IntLiteral()] Float(N) as Copy {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/int";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/operators";
|
||||
import library "prelude/types/int_literal";
|
||||
@@ -15,6 +16,8 @@ class Int(N:! IntLiteral()) {
|
||||
adapt MakeInt(N);
|
||||
}
|
||||
|
||||
impl forall [N:! IntLiteral()] Int(N) as UnformedInit {}
|
||||
|
||||
// Copy.
|
||||
|
||||
impl forall [N:! IntLiteral()] Int(N) as Copy {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package Core library "prelude/types/maybe_unformed";
|
||||
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
|
||||
private fn MakeMaybeUnformed(t: type) -> type = "maybe_unformed.make_type";
|
||||
@@ -12,3 +13,5 @@ private fn MakeMaybeUnformed(t: type) -> type = "maybe_unformed.make_type";
|
||||
class MaybeUnformed(T:! Destroy) {
|
||||
adapt MakeMaybeUnformed(T);
|
||||
}
|
||||
|
||||
impl forall [T:! Destroy] MaybeUnformed(T) as UnformedInit {}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/optional";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/operators/as";
|
||||
import library "prelude/operators/bitwise";
|
||||
@@ -40,6 +41,8 @@ class Optional(T:! OptionalStorage) {
|
||||
adapt T.Type;
|
||||
}
|
||||
|
||||
impl forall [T:! OptionalStorage & UnformedInit] Optional(T) as UnformedInit {}
|
||||
|
||||
// Support for converting a `T` to an `Optional(U)` if `T` converts to `U`.
|
||||
// Once we have match_first, this can be rewritten more simply as:
|
||||
//
|
||||
@@ -105,17 +108,23 @@ private class DefaultOptionalStorage(T:! Copy & Destroy) {
|
||||
var has_value: bool;
|
||||
}
|
||||
|
||||
private fn MakeUninitializedOptionalStorage(T:! Copy & Destroy)
|
||||
-> DefaultOptionalStorage(T) = "make_uninitialized";
|
||||
|
||||
impl forall [T:! Copy & Destroy] T as OptionalStorage
|
||||
where .Type = DefaultOptionalStorage(T) {
|
||||
fn None() -> DefaultOptionalStorage(T) {
|
||||
returned var me: DefaultOptionalStorage(T);
|
||||
returned var me: DefaultOptionalStorage(T) =
|
||||
MakeUninitializedOptionalStorage(T);
|
||||
me.has_value = false;
|
||||
return var;
|
||||
}
|
||||
fn Some[self: Self]() -> DefaultOptionalStorage(T) {
|
||||
returned var me: DefaultOptionalStorage(T);
|
||||
// TODO: Should be:
|
||||
// me.value = self as MaybeUnformed(T);
|
||||
// TODO: This whole function should be just
|
||||
// return {.value = self, .has_value = true};
|
||||
// but that requires that `T` implements `ImplicitAs(MaybeUnformed(T))`.
|
||||
returned var me: DefaultOptionalStorage(T) =
|
||||
MakeUninitializedOptionalStorage(T);
|
||||
me.value unsafe as T = self;
|
||||
me.has_value = true;
|
||||
return var;
|
||||
@@ -130,13 +139,17 @@ impl forall [T:! Copy & Destroy] T as OptionalStorage
|
||||
|
||||
private fn PointerIsNull[T:! type](value: MaybeUnformed(T*)) -> bool = "pointer.is_null";
|
||||
|
||||
private fn MakeUninitializedOptionalPointer(T:! type)
|
||||
-> MaybeUnformed(T*) = "make_uninitialized";
|
||||
|
||||
// For pointers, we use a null pointer value as the "None" value. This allows
|
||||
// `Optional(T*)` to be ABI-compatible with a C++ nullable pointer.
|
||||
final impl forall [T:! type] T* as OptionalStorage
|
||||
where .Type = MaybeUnformed(T*) {
|
||||
fn None() -> MaybeUnformed(T*) = "pointer.make_null";
|
||||
fn Some[self: Self]() -> MaybeUnformed(T*) {
|
||||
returned var result: MaybeUnformed(T*);
|
||||
returned var result: MaybeUnformed(T*) =
|
||||
MakeUninitializedOptionalPointer(T);
|
||||
result unsafe as T* = self;
|
||||
return var;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/string";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/types/char";
|
||||
import library "prelude/types/uint";
|
||||
@@ -24,6 +25,8 @@ class String {
|
||||
private var size: u64;
|
||||
}
|
||||
|
||||
impl String as UnformedInit {}
|
||||
|
||||
impl forall [T:! ImplicitAs(i64)] String as IndexWith(T) where .ElementType = Char {
|
||||
fn At[self: Self](subscript: T) -> Char = "string.at";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package Core library "prelude/types/uint";
|
||||
|
||||
import library "prelude/copy";
|
||||
import library "prelude/default";
|
||||
import library "prelude/destroy";
|
||||
import library "prelude/operators";
|
||||
import library "prelude/types/int";
|
||||
@@ -16,6 +17,8 @@ class UInt(N:! IntLiteral()) {
|
||||
adapt MakeUInt(N);
|
||||
}
|
||||
|
||||
impl forall [N:! IntLiteral()] UInt(N) as UnformedInit {}
|
||||
|
||||
// Copy.
|
||||
|
||||
impl forall [N:! IntLiteral()] UInt(N) as Copy {
|
||||
|
||||
@@ -515,15 +515,16 @@ may be limited to integers of at most 128 bits due to LLVM limitations.
|
||||
|
||||
#### Integer literals
|
||||
|
||||
Integers may be written in decimal, hexadecimal, or binary:
|
||||
Integers may be written in decimal, hexadecimal, octal, or binary:
|
||||
|
||||
- `12345` (decimal)
|
||||
- `0x1FE` (hexadecimal)
|
||||
- `0o755` (octal)
|
||||
- `0b1010` (binary)
|
||||
|
||||
Underscores (`_`) may be used as digit separators. Numeric literals are
|
||||
case-sensitive: `0x`, `0b` must be lowercase, whereas hexadecimal digits must be
|
||||
uppercase. Integer literals never contain a `.`.
|
||||
case-sensitive: `0x`, `0o`, `0b` must be lowercase, whereas hexadecimal digits
|
||||
must be uppercase. Integer literals never contain a `.`.
|
||||
|
||||
Unlike in C++, literals do not have a suffix to indicate their type. Instead,
|
||||
numeric literals have a type derived from their value, and can be
|
||||
|
||||
@@ -27,6 +27,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Return type](#return-type)
|
||||
- [Interfaces recap](#interfaces-recap)
|
||||
- [Facet types](#facet-types)
|
||||
- [Identified facet types](#identified-facet-types)
|
||||
- [Named constraints](#named-constraints)
|
||||
- [Subtyping between facet types](#subtyping-between-facet-types)
|
||||
- [Combining interfaces by anding facet types](#combining-interfaces-by-anding-facet-types)
|
||||
@@ -894,6 +895,25 @@ names of the facet type.
|
||||
This general structure of facet types holds not just for interfaces, but others
|
||||
described in the rest of this document.
|
||||
|
||||
### Identified facet types
|
||||
|
||||
A facet type is _identified_ if all the interfaces it references are declared
|
||||
and all of its named constraints are complete. An identified facet type is
|
||||
associated with a known set of interfaces.
|
||||
|
||||
A facet type is _partially identified_ if any of its named constraints are in
|
||||
the process of being defined. The interfaces associated with a partially
|
||||
identified facet type change as the named constraint is fully defined.
|
||||
|
||||
Types can implicitly convert to facet types when the requirements of the facet
|
||||
type are satisfied, but only if the facet type is identified. Attempting to
|
||||
convert to a facet type that is not identified is an error, since the
|
||||
requirements of the target facet type are not yet fully determined.
|
||||
|
||||
A facet with an unidentified or partially identified facet type may be converted
|
||||
_to_ other facet types. While its set of requirements are not fully determined,
|
||||
the requirements that are known at that time may be used.
|
||||
|
||||
## Named constraints
|
||||
|
||||
If the interfaces discussed above are the building blocks for facet types,
|
||||
@@ -933,6 +953,54 @@ constraint DrawVectorLegoFish {
|
||||
}
|
||||
```
|
||||
|
||||
However a named constraint may not refer to itself as a requirement, as that
|
||||
produces a cycle. In general, any use of named constraint inside its own
|
||||
definition is disallowed, except through the use of `Self`.
|
||||
|
||||
```carbon
|
||||
constraint SelfReferential {
|
||||
// ❌ Error: Can not refer to `SelfReferential` inside its own definition.
|
||||
require impls SelfReferential;
|
||||
}
|
||||
```
|
||||
|
||||
The facet type of `Self` is partially identified inside the definition of a
|
||||
named constraint. This allows `Self` to be converted to other facet types based
|
||||
on the known requirements of the partially identified facet type. Those
|
||||
requirements include any `require` ... `impls` statements written before the use
|
||||
of `Self`.
|
||||
|
||||
```carbon
|
||||
interface Z {}
|
||||
class UsesZ(T:! Z) {}
|
||||
|
||||
interface Y(T:! type) {}
|
||||
interface X {}
|
||||
|
||||
constraint Constraint {
|
||||
// The partially identified facet type of `Self` includes `Z` after this
|
||||
// statement.
|
||||
require impls Z;
|
||||
|
||||
// OK, the partially identified facet type of `Self` can convert to facet
|
||||
// type `Z` to match the parameter of `UsesZ`.
|
||||
require impls Y(UsesZ(Self));
|
||||
|
||||
// Also OK, as `Self` converts to `Z` again.
|
||||
require UsesZ(Self) impls X;
|
||||
}
|
||||
|
||||
constraint UseOfFutureRequirement {
|
||||
// ❌ Error: The partially identified facet type of `Self` does not yet
|
||||
// include `Z` since the requirement for `Z` comes later in the definition.
|
||||
require impls Y(UsesZ(Self));
|
||||
|
||||
// The partially identified facet type of `Self` includes `Z` after this
|
||||
// statement.
|
||||
require impls Z;
|
||||
}
|
||||
```
|
||||
|
||||
In general, Carbon makes no syntactic distinction between the uses of named
|
||||
constraints and interfaces, so one may be replaced with the other without
|
||||
affecting users. To accomplish this, Carbon allows a named constraint to be used
|
||||
@@ -1229,6 +1297,27 @@ var x: Iota;
|
||||
DoAdvanceAndEquals(x);
|
||||
```
|
||||
|
||||
The facet type at the end of a `require` ... `impls` statement must be
|
||||
identified.
|
||||
|
||||
```carbon
|
||||
constraint N;
|
||||
|
||||
interface I {
|
||||
// ❌ Error: Facet type `N` is not identified since the constraint `N` is not
|
||||
// complete.
|
||||
require impls N;
|
||||
}
|
||||
|
||||
interface J;
|
||||
|
||||
interface K {
|
||||
// OK, the facet type `J` is identified because the interface `J` is
|
||||
// declared.
|
||||
require impls J;
|
||||
}
|
||||
```
|
||||
|
||||
Like with named constraints, an interface implementation requirement doesn't by
|
||||
itself add any names to the interface, but again those can be added with `alias`
|
||||
declarations:
|
||||
|
||||
@@ -28,6 +28,7 @@ The following syntaxes are supported:
|
||||
- [Integer literals](#integer-literals)
|
||||
- `12345` (decimal)
|
||||
- `0x1FE` (hexadecimal)
|
||||
- `0o755` (octal)
|
||||
- `0b1010` (binary)
|
||||
- [Real-number literals](#real-number-literals)
|
||||
- `123.456` (digits on both sides of the `.`)
|
||||
@@ -55,15 +56,16 @@ base. The available base specifiers and corresponding bases are:
|
||||
| Base specifier | Base | Digits |
|
||||
| -------------- | ---- | ------------------------ |
|
||||
| `b` | 2 | `0` and `1` |
|
||||
| `o` | 8 | `0` ... `7` |
|
||||
| `x` | 16 | `0` ... `9`, `A` ... `F` |
|
||||
|
||||
The above table is case-sensitive. For example, `0b1` and `0x1A` are valid, and
|
||||
`0B1`, `0X1A`, and `0x1a` are invalid.
|
||||
The above table is case-sensitive. For example, `0b1`, `0o7`, and `0x1A` are
|
||||
valid, and `0B1`, `0O7`, `0X1A`, and `0x1a` are invalid.
|
||||
|
||||
A zero at the start of a literal can never be followed by another digit: either
|
||||
the literal is `0`, the `0` begins a base specifier, or the next character is a
|
||||
decimal point (see below). No support is provided for octal literals, and any C
|
||||
or C++ octal literal (other than `0`) is invalid in Carbon.
|
||||
decimal point (see below). The `0o` prefix is used for octal literals; a C-style
|
||||
`0755` octal is invalid in Carbon.
|
||||
|
||||
### Real-number literals
|
||||
|
||||
@@ -111,8 +113,9 @@ example:
|
||||
|
||||
- Decimal integers: `1_23_456_7890`
|
||||
- Hexadecimal integers: `0x7_F_FF_FFFF`
|
||||
- Real-number literals: `2_147.48_3648e12_345` or `0x1_00CA.FE_F00Dp+2_4`
|
||||
- Octal literals: `0o7_55`
|
||||
- Binary literals: `0b1_000_101_11`
|
||||
- Real-number literals: `2_147.48_3648e12_345` or `0x1_00CA.FE_F00Dp+2_4`
|
||||
|
||||
## Divergence from other languages
|
||||
|
||||
@@ -129,7 +132,7 @@ provides benefits directly in line with the goal that Carbon code should be
|
||||
That said, it still provides sufficient variations to address important use
|
||||
cases for the goal of not leaving room for a lower level language:
|
||||
|
||||
- Hexadecimal and binary integer literals.
|
||||
- Hexadecimal, octal, and binary integer literals.
|
||||
- Scientific notation floating point literals.
|
||||
- Hexadecimal (scientific) floating point literals.
|
||||
|
||||
@@ -145,6 +148,7 @@ cases for the goal of not leaving room for a lower level language:
|
||||
- [3-digit decimal groupings](/proposals/p1983.md#3-digit-decimal-groupings)
|
||||
- [2-digit or 4-digit hexadecimal digit groupings](/proposals/p1983.md#2-digit-or-4-digit-hexadecimal-digit-groupings)
|
||||
- [Disallow digit separators in fractions](/proposals/p1983.md#disallow-digit-separators-in-fractions)
|
||||
- [No octal literals](/proposals/p6910.md#no-octal-literals)
|
||||
|
||||
## References
|
||||
|
||||
@@ -154,3 +158,5 @@ cases for the goal of not leaving room for a lower level language:
|
||||
[#866: Allow ties in floating literals](https://github.com/carbon-language/carbon-lang/pull/866)
|
||||
- Proposal
|
||||
[#1983: Weaken digit separator placement rules](https://github.com/carbon-language/carbon-lang/pull/1983)
|
||||
- Proposal
|
||||
[#6910: Support octal literals](https://github.com/carbon-language/carbon-lang/pull/6910)
|
||||
|
||||
@@ -23,6 +23,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
- [Responding to review comments](#responding-to-review-comments)
|
||||
- [Responding to questions or confusion](#responding-to-questions-or-confusion)
|
||||
- [Understand the feedback in the comments](#understand-the-feedback-in-the-comments)
|
||||
- [Fixing conflicts with trunk](#fixing-conflicts-with-trunk)
|
||||
- [Code reviewer guide](#code-reviewer-guide)
|
||||
- [How quickly should you respond to a review request?](#how-quickly-should-you-respond-to-a-review-request)
|
||||
- [What should be covered by a review?](#what-should-be-covered-by-a-review)
|
||||
@@ -325,6 +326,13 @@ long delays between these iterations. Consider discussing over Discord chat or
|
||||
scheduling a quick video chat on the specific issue. This can avoid multi-hour
|
||||
-- or multi-day -- round trips.
|
||||
|
||||
### Fixing conflicts with trunk
|
||||
|
||||
If a PR has conflicts with trunk, those conflicts must be resolved before the PR
|
||||
can be merged. If the PR is already in review, prefer to wait until review is
|
||||
mostly done before fixing the conflicts. Conflicts should be fixed by way of a
|
||||
merge commit rather than rebasing.
|
||||
|
||||
## Code reviewer guide
|
||||
|
||||
The specific goal for a particular review should always be to ensure that the
|
||||
|
||||
@@ -24,6 +24,7 @@ contributions.
|
||||
- [Optional tools](#optional-tools)
|
||||
- [Jujutsu (`jj`)](#jujutsu-jj)
|
||||
- [AI assistants](#ai-assistants)
|
||||
- [Running tests with AddressSanitizer (ASan)](#running-tests-with-addresssanitizer-asan)
|
||||
- [Manually building Clang and LLVM (not recommended)](#manually-building-clang-and-llvm-not-recommended)
|
||||
- [Troubleshooting build issues](#troubleshooting-build-issues)
|
||||
- [`bazel clean`](#bazel-clean)
|
||||
@@ -256,31 +257,50 @@ system that can be used instead of or alongside Git. See the
|
||||
for more information.
|
||||
|
||||
If you use `jj`, you may find the following configuration snippets (added to
|
||||
`~/.config/jj/config.toml`) helpful for your workflow:
|
||||
`jj config path --user`) helpful for your workflow:
|
||||
|
||||
```toml
|
||||
[aliases]
|
||||
```sh
|
||||
# Clean up untracked or abandoned commits.
|
||||
abandon-untagged = ["abandon", "all() & ~ancestors(@ | bookmarks() | remote_bookmarks())"]
|
||||
jj config set --user aliases.abandon-untagged '["abandon", "all() & ~ancestors(@ | bookmarks() | remote_bookmarks())"]'
|
||||
|
||||
[ui]
|
||||
# Use Git-style conflict markers, which VS Code can provide merge support for.
|
||||
conflict-marker-style = "git"
|
||||
jj config set --user ui.conflict-marker-style 'git'
|
||||
|
||||
[ui.diff]
|
||||
# Produce Git-compatible diff format.
|
||||
format = "git"
|
||||
jj config set --user ui.diff.format 'git'
|
||||
|
||||
[remotes.origin]
|
||||
# Automatically track all remote bookmarks.
|
||||
auto-track-bookmarks = "*"
|
||||
|
||||
[templates]
|
||||
# Automatically add a trailer to commits to indicate that they were AI-assisted.
|
||||
commit_trailers = '''
|
||||
"Assisted-by: My AI Tool"'''
|
||||
jj config set --user templates.commit_trailers "$(echo -e "'''\n\"Assisted-by: My AI Tool\"'''")"
|
||||
|
||||
# Make `jj bookmark advance` / `jj b a` only move bookmarks that point to
|
||||
# mutable commits, and move them to the most recent non-empty descendant.
|
||||
jj config set --user revsets.bookmark-advance-from 'heads(::to & bookmarks()) & ~immutable_heads()'
|
||||
jj config set --user revsets.bookmark-advance-to 'heads(::@ & ~(description("") & empty() & ~merges()))'
|
||||
```
|
||||
|
||||
<!-- google-doc-style-ignore -->
|
||||
|
||||
As well as this per-repository configuration (added to `jj config path --repo`)
|
||||
describing how your GitHub checkout is configured:
|
||||
|
||||
```sh
|
||||
# Automatically track all remote bookmarks.
|
||||
jj config set --repo remotes.origin.auto-track-bookmarks '*'
|
||||
|
||||
# `trunk()` is a jj builtin, but defaults to `main@upstream`.
|
||||
jj config set --repo 'revset-aliases."trunk()"' 'trunk@upstream'
|
||||
|
||||
# Treat github.com/carbon-language/carbon-lang as immutable, but treat your fork
|
||||
# as mutable.
|
||||
jj config set --repo 'revset-aliases."immutable_heads()"' 'remote_bookmarks(*, upstream)'
|
||||
```
|
||||
|
||||
<!-- google-doc-style-resume -->
|
||||
|
||||
The above assumes that you have configured the remote name `origin` to refer to
|
||||
your fork and `upstream` to refer to `github.com/carbon-language/carbon-lang`,
|
||||
and will need to be adjusted if you use different remote names.
|
||||
|
||||
#### AI assistants
|
||||
|
||||
When using AI assistants and reviewing terminal commands, some commands which
|
||||
@@ -310,6 +330,20 @@ git show
|
||||
git status
|
||||
```
|
||||
|
||||
### Running tests with AddressSanitizer (ASan)
|
||||
|
||||
By default, the Bazel build mode for the toolchain does not enable
|
||||
AddressSanitizer (ASan). If you wish to enable ASan for local testing, you must
|
||||
pass the `--config=asan` flag explicitly:
|
||||
|
||||
```shell
|
||||
bazelisk test --config=asan //...
|
||||
```
|
||||
|
||||
Note that our Continuous Integration (CI) infrastructure runs a separate
|
||||
configuration for ASan to ensure test coverage without slowing down the default
|
||||
test cycle.
|
||||
|
||||
### Manually building Clang and LLVM (not recommended)
|
||||
|
||||
We primarily test against [apt.llvm.org](https://apt.llvm.org) and Homebrew
|
||||
|
||||
@@ -11,6 +11,8 @@ import Core library "range";
|
||||
import library "io_utils";
|
||||
|
||||
class Terrain {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Terrain {
|
||||
returned var me: Terrain;
|
||||
for (y: i32 in Core.Range(43)) {
|
||||
|
||||
@@ -24,6 +24,8 @@ fn PopCount(n: u256) -> i32 {
|
||||
}
|
||||
|
||||
class Reachable {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Make(terrain: Terrain) -> Reachable {
|
||||
returned var me: Reachable;
|
||||
var next: u256 = 1;
|
||||
|
||||
@@ -11,6 +11,8 @@ import library "day10_common";
|
||||
import library "io_utils";
|
||||
|
||||
class PathsToTop {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Make(terrain: Terrain) -> PathsToTop {
|
||||
returned var me: PathsToTop;
|
||||
for (y: i32 in Core.Range(43)) {
|
||||
|
||||
@@ -11,6 +11,8 @@ import library "day11_common";
|
||||
import library "io_utils";
|
||||
|
||||
class Digits {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Make() -> Digits {
|
||||
returned var me: Digits;
|
||||
for (digit: i32 in Core.Range(10)) {
|
||||
|
||||
@@ -11,6 +11,8 @@ import Core library "range";
|
||||
import library "io_utils";
|
||||
|
||||
class Map {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Map {
|
||||
returned var me: Self;
|
||||
for (y: i32 in Core.Range(140)) {
|
||||
@@ -32,6 +34,8 @@ class Map {
|
||||
}
|
||||
|
||||
class DisjointSetForest {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Make() -> DisjointSetForest {
|
||||
returned var me: Self;
|
||||
for (i: i32 in Core.Range(140 * 140)) {
|
||||
|
||||
@@ -23,6 +23,8 @@ fn Euclid(a: i64, b: i64) -> {.m: i64, .n: i64, .gcd: i64} {
|
||||
}
|
||||
|
||||
class Machine {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Machine {
|
||||
returned var me: Machine;
|
||||
// "Button A: X+"
|
||||
|
||||
@@ -23,6 +23,8 @@ fn Mod(a: i32, d: i32) -> i32 {
|
||||
}
|
||||
|
||||
class Robot {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Robot {
|
||||
returned var me: Robot;
|
||||
SkipString("p=");
|
||||
|
||||
@@ -45,6 +45,8 @@ let Empty: Square = ('.' as char) as Square;
|
||||
let Robot: Square = ('@' as char) as Square;
|
||||
|
||||
class Grid {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Grid {
|
||||
returned var me: Grid;
|
||||
var y: i32 = 0;
|
||||
|
||||
@@ -9,6 +9,8 @@ library "day4_common";
|
||||
import library "io_utils";
|
||||
|
||||
class Wordsearch {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Wordsearch {
|
||||
returned var s: Wordsearch;
|
||||
// TODO: Use for loops once they're implemented.
|
||||
|
||||
@@ -16,6 +16,8 @@ fn PageMask(page: i32) -> Core.UInt(100) {
|
||||
}
|
||||
|
||||
class Rules {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Rules {
|
||||
returned var rules: Rules;
|
||||
for (i: i32 in Core.Range(100)) {
|
||||
@@ -39,6 +41,8 @@ class Rules {
|
||||
};
|
||||
|
||||
class PageList {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Empty() -> PageList {
|
||||
returned var me: PageList;
|
||||
me.num_pages = 0;
|
||||
|
||||
@@ -15,6 +15,8 @@ fn Visited() -> i8 { return 1; }
|
||||
fn Wall() -> i8 { return 2; }
|
||||
|
||||
class Maze {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Maze {
|
||||
returned var me: Maze;
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ fn Concat(a_val: i64, b_val: i64) -> i64 {
|
||||
}
|
||||
|
||||
class Equation {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Equation {
|
||||
returned var me: Equation;
|
||||
me.num_operands = 0;
|
||||
|
||||
@@ -11,6 +11,8 @@ import Core library "range";
|
||||
import library "io_utils";
|
||||
|
||||
class Grid {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> Grid {
|
||||
returned var me: Grid;
|
||||
for (y: i32 in Core.Range(50)) {
|
||||
|
||||
@@ -11,6 +11,8 @@ import Core library "range";
|
||||
import library "io_utils";
|
||||
|
||||
class SectorList {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Read() -> SectorList {
|
||||
returned var me: SectorList;
|
||||
me.size = 0;
|
||||
|
||||
@@ -21,7 +21,7 @@ class BazelExampleTest(test_base.TestBase):
|
||||
test_base.TestBase.setUp(self)
|
||||
self.runfiles = runfiles.Create()
|
||||
self.install_module = self.runfiles.Rlocation(
|
||||
"carbon/toolchain/install/prefix/lib/carbon"
|
||||
"carbon/toolchain/install"
|
||||
)
|
||||
self.startup_flags = [
|
||||
"--ignore_all_rc_files",
|
||||
@@ -62,7 +62,9 @@ class BazelExampleTest(test_base.TestBase):
|
||||
# Retry transient errors with a brief delay.
|
||||
print(f"Attempt {attempt + 1} failed with exit code {exit_code}")
|
||||
time.sleep(attempt)
|
||||
|
||||
self.AssertExitCode(exit_code, 0, stderr)
|
||||
return stdout
|
||||
|
||||
def test_compile_lib(self) -> None:
|
||||
# TODO: Can remove this in favor of always running `test_run` if we can
|
||||
|
||||
@@ -40,7 +40,7 @@ fn Run() -> i32 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: Should be able to zero-initialize this with `= ()` or `= {}`.
|
||||
// TODO: We should be able to use `= ()` or `= {}` to perform zero-initialization.
|
||||
var address: Cpp.sockaddr_in6;
|
||||
Cpp.memset(&address, 0, Cpp.SOCKADDR_IN_SIZE);
|
||||
// TODO: Should this be valid without a cast? `AF_INET6` is defined to an
|
||||
|
||||
@@ -8,6 +8,8 @@ import Core library "range";
|
||||
// Compute and return the number of primes less than 1000.
|
||||
|
||||
class Sieve {
|
||||
impl as Core.UnformedInit {}
|
||||
|
||||
fn Make() -> Sieve {
|
||||
returned var s: Sieve;
|
||||
for (n: i32 in Core.Range(1000)) {
|
||||
|
||||
@@ -117,9 +117,9 @@ promise==2.3 \
|
||||
# via
|
||||
# gql
|
||||
# graphql-core
|
||||
requests==2.32.4 \
|
||||
--hash=sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c \
|
||||
--hash=sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422
|
||||
requests==2.33.0 \
|
||||
--hash=sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b \
|
||||
--hash=sha256:c7ebc5e8b0f21837386ad0e1c8fe8b829fa5f544d8df3b2253bff14ef29d7652
|
||||
# via gql
|
||||
rx==1.6.3 \
|
||||
--hash=sha256:ca71b65d0fc0603a3b5cfaa9e33f5ba81e4aae10a58491133595088d7734b2da
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
# Identification of a named constraint during definition
|
||||
|
||||
<!--
|
||||
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/6902)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Example with `Self`](#example-with-self)
|
||||
- [Motivating the partially identified state](#motivating-the-partially-identified-state)
|
||||
- [Disallowing conversions to incomplete named constraint](#disallowing-conversions-to-incomplete-named-constraint)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [Considering a facet type of a named constraint to be identified in its definition](#considering-a-facet-type-of-a-named-constraint-to-be-identified-in-its-definition)
|
||||
- [Restricting to `Self`](#restricting-to-self)
|
||||
- [Allowing limited conversions to partially identified facet types.](#allowing-limited-conversions-to-partially-identified-facet-types)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
This proposal updates the criteria for when a facet type is considered
|
||||
"identified." Specifically, it relaxes the requirement for named constraints,
|
||||
allowing them to be incrementally identified inside the definition, rather than
|
||||
requiring them to be fully complete, when used through the `Self` facet. This
|
||||
change enables impl lookups with `Self` within a constraint's definition to
|
||||
correctly resolve witnesses based on prior `require impls` statements in the
|
||||
definition.
|
||||
|
||||
## Problem
|
||||
|
||||
Under the rules established in [Proposal #5168](/proposals/p5168.md), a facet
|
||||
type is identified only if all its referenced interfaces are declared and all
|
||||
its referenced named constraints are complete.
|
||||
|
||||
This definition creates a circularity problem during the definition of a named
|
||||
constraint. If a `require impls` statement inside a named constraint definition
|
||||
relies on an impl lookup with `Self`, that lookup will fail because the facet
|
||||
type of `Self` is not identified before the named constraint is complete. This
|
||||
prevents `require impls` statements in a named constraint from depending on
|
||||
earlier ones.
|
||||
|
||||
## Background
|
||||
|
||||
- [Proposal #5168](/proposals/p5168.md): Introduced rules for facet type
|
||||
identification and completion.
|
||||
|
||||
## Proposal
|
||||
|
||||
We propose redefining the identification criteria for facet types by introducing
|
||||
a new partially identified state.
|
||||
|
||||
A facet type can be in one of three states: unidentified, partially identified,
|
||||
or identified. A facet type's identifiedness is the minimum of that of its
|
||||
constituents:
|
||||
|
||||
- When a facet type refers to an interface, the facet type is not identified
|
||||
until the interface is declared, and is fully identified after. This
|
||||
includes inside the definition of the interface.
|
||||
- When a facet type refers to a named constraint, the facet type is not
|
||||
identified until the named constraint is declared. It is partially
|
||||
identified during the definition of the named constraint, and it is fully
|
||||
identified after.
|
||||
|
||||
The change from previous rules is that a facet type containing a named
|
||||
constraint is now partially identified inside the definition of that named
|
||||
constraint.
|
||||
|
||||
As in [#5168](/proposals/p5168.md), an `impl` declaration and `require`
|
||||
statement each requires its constraint to be identified.
|
||||
|
||||
We define the rules for facets in impl lookups, which are representable as
|
||||
`<self> as <target facet type>` conversions as follows:
|
||||
|
||||
- The target facet type of an impl lookup must be defined.
|
||||
- This allows the full set of interfaces to be known, which allows a
|
||||
stable ordering of witnesses for those interfaces to be produced by the
|
||||
impl lookup.
|
||||
- If the self is a facet, its facet type may be in any state of
|
||||
identifiedness.
|
||||
- The impl lookup may provide a witness from the facet type of self, using
|
||||
the known constraints of any partially identified or identified
|
||||
constituent of the facet type.
|
||||
|
||||
In particular, this means that `Self as I` inside the definition of a named
|
||||
constraint `N` may use any `require impls` statements before that use of `Self`
|
||||
in order to provide a witness for `I`.
|
||||
|
||||
To improve diagnostics, we also propose to disallow using a named constraint
|
||||
inside its own definition, except through the type of `Self`. Any other use is
|
||||
diagnosed as an error. This provides a clear error when a named constraint
|
||||
appears in the constraint of a `require` statement inside its definition.
|
||||
|
||||
### Example with `Self`
|
||||
|
||||
This change allows the compiler to treat a named constraint as partially
|
||||
identified for the purposes of impl lookup while it is still being typechecked.
|
||||
|
||||
As the compiler processes a series of `require impls` statements within a named
|
||||
constraint, the partially identified facet type of the named constraint, which
|
||||
can be accessed through `Self`, is built up incrementally. The partially
|
||||
identified facet type of `Self` will contain interfaces provided by
|
||||
`require impls` statements written prior to that use of `Self`. Thus later
|
||||
`require impls` statements can use the partially identified facet type of `Self`
|
||||
to find witnesses provided by earlier `require impls` statements during impl
|
||||
lookup.
|
||||
|
||||
The following example demonstrates a scenario that is currently invalid but
|
||||
would be enabled by this proposal:
|
||||
|
||||
```carbon
|
||||
interface Y {}
|
||||
|
||||
interface NeedsY(T:! Y) {}
|
||||
|
||||
constraint W {
|
||||
require impls Y;
|
||||
|
||||
// This requires an impl lookup where the query self value is `Self`
|
||||
// (which is of type `W`) and the query interface is `Y`. The lookup
|
||||
// requires identifying the facet type of `Self` to find a witness.
|
||||
// After this proposal, W is partially identified because it has begun being
|
||||
// defined. The lookup for `Self as Y` can now succeed due to the previous
|
||||
// `require impls` statement.
|
||||
require impls NeedsY(Self);
|
||||
}
|
||||
```
|
||||
|
||||
In this example, identifying `W` while it is being defined allows the lookup for
|
||||
`Self as Y` in order to form a facet value for `NeedsY` to succeed because the
|
||||
compiler knows `Self` (of type `W`) implements `Y` from the previous
|
||||
`require impls` statement.
|
||||
|
||||
### Motivating the partially identified state
|
||||
|
||||
If a facet type for a named constraint was considered identified (not partially
|
||||
identified) inside its definition, the following becomes possible:
|
||||
|
||||
```carbon
|
||||
constraint W {
|
||||
require C impls W;
|
||||
require impls Z;
|
||||
}
|
||||
```
|
||||
|
||||
This says that `C` must implement `W`, yet `W` is not fully defined. At that
|
||||
line `W` is still empty, so it places no requirements on `C`. The next line
|
||||
requires that anything implementing `W` must implement `Z`.
|
||||
|
||||
To prevent this, the constraint of a `require` statement must still be
|
||||
identified, and the facet type of the being-defined named constraint is only
|
||||
partially-identified.
|
||||
|
||||
Note this also disallows the use of `W` through an alias:
|
||||
|
||||
```carbon
|
||||
constraint W;
|
||||
alias X = W;
|
||||
constraint W {
|
||||
// Error: X refers to named constraint `W` that is not identified.
|
||||
require impls X;
|
||||
}
|
||||
```
|
||||
|
||||
### Disallowing conversions to incomplete named constraint
|
||||
|
||||
By requiring the target facet type of an impl lookup to be identified, we
|
||||
disallow an incomplete named constraint from being part of the target of an impl
|
||||
lookup.
|
||||
|
||||
The result of an impl lookup stores witnesses for the target facet type. If the
|
||||
target facet type was partially identified, the same facet type may have a
|
||||
different set of interfaces later. A change in the set of interfaces would
|
||||
invalidate the set of stored witnesses.
|
||||
|
||||
For example, if this was allowed:
|
||||
|
||||
```carbon
|
||||
interface Z {}
|
||||
interface X {}
|
||||
|
||||
constraint W;
|
||||
class C(T:! W) {}
|
||||
class D {}
|
||||
|
||||
interface Y(T:! type) {}
|
||||
|
||||
constraint W {
|
||||
require impls Z;
|
||||
// Constructs a `D as W` facet value.
|
||||
require impls Y(C(D));
|
||||
require impls X;
|
||||
}
|
||||
```
|
||||
|
||||
In this example the argument to `C` will be `D as W` which will store a witness
|
||||
for each interface in the identified facet type `W`. If we allow a partially
|
||||
identified facet type, then it will store a witness for the interface `Z`. But
|
||||
later uses of the facet value in `Y(C(D))` would expect witnesses for `Z`, `Y`
|
||||
and `X`, leading to unsoundness.
|
||||
|
||||
## Rationale
|
||||
|
||||
This change aligns with Carbon's goal of
|
||||
[Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write),
|
||||
by allowing expressive generics as they have been designed.
|
||||
|
||||
This follows the
|
||||
[Information accumulation](/docs/project/principles/information_accumulation.md)
|
||||
principle by increasing the information available to the program with each
|
||||
statement.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### Considering a facet type of a named constraint to be identified in its definition
|
||||
|
||||
Initial versions of this proposal did not differentiate between partially
|
||||
identified and identified. This led to unsoundness by allowing conversion to a
|
||||
facet with the incomplete named constraint in the facet's type, as described in
|
||||
[Disallowing conversions to incomplete named constraint](#disallowing-conversions-to-incomplete-named-constraint).
|
||||
|
||||
### Restricting to `Self`
|
||||
|
||||
We considered restricting the use of the partially identified facet type to only
|
||||
be on the `Self` facet value. This provided a way to reduce exposure of the
|
||||
partially identified facet type. But by differentiating the the partially
|
||||
identified state from identified, we can form the rules around the state of the
|
||||
facet type instead of the identity of the facet.
|
||||
|
||||
### Allowing limited conversions to partially identified facet types.
|
||||
|
||||
We considered allowing conversions from `N & J` to `N & K` where `N` is
|
||||
partially identified, and `J` and `K` are identified.
|
||||
|
||||
It seems possible to support this for symbolic facets, by not storing the set of
|
||||
witnesses in the facet value. If the facet type is partially identified, we can
|
||||
defer the collection of witnesses, and just store the facet types that the facet
|
||||
was converted from. In this model, the facet type itself acts as a type of
|
||||
witness that we will will be able to find a witness later once the facet type is
|
||||
identified.
|
||||
|
||||
We leave this to a future proposal if and when we find this additional
|
||||
complexity worth adding to the language model.
|
||||
@@ -0,0 +1,107 @@
|
||||
# Support octal literals
|
||||
|
||||
<!--
|
||||
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/6910)
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Abstract](#abstract)
|
||||
- [Problem](#problem)
|
||||
- [Background](#background)
|
||||
- [Proposal](#proposal)
|
||||
- [Future work](#future-work)
|
||||
- [File permissions API](#file-permissions-api)
|
||||
- [Rationale](#rationale)
|
||||
- [Alternatives considered](#alternatives-considered)
|
||||
- [No octal literals](#no-octal-literals)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Abstract
|
||||
|
||||
Support octal literals, mainly for migrating Unix file permissions. Reflects
|
||||
leads decision
|
||||
[#6821](https://github.com/carbon-language/carbon-lang/issues/6821).
|
||||
|
||||
## Problem
|
||||
|
||||
Carbon currently does not support octal numeric literals, because they're very
|
||||
rare, as previously decided in proposal
|
||||
[#143: Numeric literals](https://github.com/carbon-language/carbon-lang/pull/143).
|
||||
However, as part of interoperability with POSIX file system calls such as
|
||||
[`umask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html),
|
||||
we want an easy way to express octal file permissions.
|
||||
|
||||
## Background
|
||||
|
||||
Leads discussed support of octal literals in issue
|
||||
[#6821: Support octal literals](https://github.com/carbon-language/carbon-lang/issues/6821).
|
||||
|
||||
Unix file permissions written in octal are familiar to both programmers and
|
||||
non-programmers who have experience administering Unix-like machines. For
|
||||
example:
|
||||
|
||||
- `chmod OCTAL-MODE FILE...`
|
||||
- [POSIX `mode_t` values](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html),
|
||||
including the argument to
|
||||
[`umask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html).
|
||||
|
||||
However, they are are not likely to be readable to those unfamiliar with them,
|
||||
nor are they very common in code. We also expect these to be the primary use of
|
||||
octal numeric literals in Carbon.
|
||||
|
||||
Given these issues, proposal #143
|
||||
[rejected octal literals](/proposals/p0143.md#octal-literals). Now, we're
|
||||
testing interoperability of POSIX file system calls, and we are considering
|
||||
octal literals as a potential solution.
|
||||
|
||||
Proposal #143 [discussed the Carbon-style `0o` versus C++-style `0` prefix for
|
||||
octal literals. The same logic still applies, so we will not address it here.
|
||||
|
||||
## Proposal
|
||||
|
||||
Introduce support for octal literals using the `0o` prefix (for example,
|
||||
`0o755`), followed by one or more octal digits (`0-7`). This provides a very
|
||||
simple lexical space for octal numbers, mapping clearly from C++ and building
|
||||
consistently off the existing `0x...` syntax for hexadecimal and `0b...` syntax
|
||||
for binary.
|
||||
|
||||
## Future work
|
||||
|
||||
### File permissions API
|
||||
|
||||
We may still provide a file permissions API in Carbon, for example as part of a
|
||||
`Core` file system API. This proposal takes no stance on what that API should
|
||||
look like. The only decision being made right now is that supporting octal
|
||||
literals is worthwhile for interoperability and migration.
|
||||
|
||||
## Rationale
|
||||
|
||||
This proposal effectively advances Carbon's goals by focusing on:
|
||||
|
||||
- [Interoperability with and migration from existing C++ code](/docs/project/goals.md#interoperability-with-and-migration-from-existing-c-code):
|
||||
Providing a direct counterpart for C++ octal literals simplifies the
|
||||
migration of Unix file system code without needing to wait for a better file
|
||||
permission API.
|
||||
- [Code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write):
|
||||
The explicit `0o` prefix avoids the frequent confusion caused by C++'s
|
||||
leading `0` syntax, while maintaining consistency with hex and binary
|
||||
prefixes.
|
||||
- [Software and language evolution](/docs/project/goals.md#software-and-language-evolution):
|
||||
The `0o` octal literal syntax is consistent with other literals. We don't
|
||||
expect it to hinder future language features.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
### No octal literals
|
||||
|
||||
Proposal #143 rejected octal literals. The main argument was that they are
|
||||
rarely used. However, the cost of octal literal syntax is low, and the benefit
|
||||
for C++ interoperability and migration is enough that we should add them.
|
||||
@@ -194,6 +194,16 @@ def main() -> None:
|
||||
assert jj_bin # For mypy.
|
||||
_run([jj_bin, "new", parsed_args.branch_start_point])
|
||||
_run([jj_bin, "bookmark", "create", branch])
|
||||
_run(
|
||||
[
|
||||
jj_bin,
|
||||
"bookmark",
|
||||
"track",
|
||||
branch,
|
||||
"--remote",
|
||||
parsed_args.remote,
|
||||
]
|
||||
)
|
||||
|
||||
# Copy template.md to a temp file.
|
||||
template_path = "proposals/scripts/template.md"
|
||||
|
||||
+1
-1
@@ -4,5 +4,5 @@
|
||||
|
||||
[tool.black]
|
||||
line-length = 80
|
||||
target-version = ['py36', 'py37', 'py38']
|
||||
target-version = ['py310']
|
||||
include = '\.pyi?$'
|
||||
|
||||
@@ -24,7 +24,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any, Dict
|
||||
@@ -78,6 +77,9 @@ def _build_generated_files(
|
||||
# We also need the Bazel C++ runfiles that aren't "generated", but are
|
||||
# not linked into place until built.
|
||||
+ ["@bazel_tools//tools/cpp/runfiles:runfiles"]
|
||||
# Also include any deps that require `_virtual_includes` to be fully
|
||||
# populated through a special C++ rule.
|
||||
+ ["//scripts:deps_for_clangd_tidy"],
|
||||
)
|
||||
|
||||
|
||||
@@ -97,44 +99,6 @@ def _get_config_for_entry(entry: Dict[str, Any]) -> str:
|
||||
return str(obj_file.split("/")[1])
|
||||
|
||||
|
||||
def _filter_compilation_database(file_path: str) -> None:
|
||||
"""Filters out duplicate exec-config entries from the database."""
|
||||
print("Filtering out duplicate exec-configuration entries...")
|
||||
try:
|
||||
with open(file_path, "r") as f:
|
||||
commands = json.load(f)
|
||||
except FileNotFoundError:
|
||||
print(f"Error: The file '{file_path}' was not found.")
|
||||
sys.exit(1)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Error: The file '{file_path}' is not a valid JSON file.")
|
||||
sys.exit(1)
|
||||
|
||||
# We want to skip compiles that were in the "exec" configuration for tools.
|
||||
# Because we generate compile commands for every bazel cc_* target in the
|
||||
# main configuration, even if only used by tools, their sources should be
|
||||
# covered and the exec configuration would simply be a duplicate.
|
||||
#
|
||||
# Detecting this based on the `-exec-` string in the configuration name of
|
||||
# the directory is a bit of a hack, but even using the `--notool_deps`
|
||||
# argument, Bazel seems to sometimes include this configuration in the query
|
||||
# that produces the compilation database.
|
||||
filtered_commands = [
|
||||
entry
|
||||
for entry in commands
|
||||
if "-exec-" not in _get_config_for_entry(entry)
|
||||
]
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
# Use indent=4 for a human-readable, pretty-printed output file
|
||||
json.dump(filtered_commands, f, indent=4)
|
||||
print(
|
||||
"Filtered out "
|
||||
f"{len(commands) - len(filtered_commands)} "
|
||||
"duplicate entries..."
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
@@ -179,17 +143,12 @@ def main() -> None:
|
||||
]
|
||||
+ args.extra_bazel_flag
|
||||
+ [
|
||||
"@hedron_compile_commands//:refresh_all",
|
||||
"//:generate_compile_commands",
|
||||
"--",
|
||||
]
|
||||
+ args.extra_bazel_flag
|
||||
+ [
|
||||
"--notool_deps",
|
||||
]
|
||||
+ [f"--extra_aquery_arg={arg}" for arg in args.extra_bazel_flag]
|
||||
)
|
||||
|
||||
_filter_compilation_database("compile_commands.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -295,6 +295,18 @@ def main() -> None:
|
||||
all_missing_deps: list[tuple[str, set[str]]] = []
|
||||
any_ambiguous = False
|
||||
for rule_name, rule in carbon_rules.items():
|
||||
# Skip rules building runtimes as the rules that provide their sources
|
||||
# are not analyzed by this script.
|
||||
if rule_name in [
|
||||
"//toolchain/install:builtins",
|
||||
"//toolchain/install:builtins_internal",
|
||||
"//toolchain/install:libc_internal_libcxx",
|
||||
"//toolchain/install:libcxx",
|
||||
"//toolchain/install:libcxxabi_internal",
|
||||
"//toolchain/install:libcxxabi",
|
||||
"//toolchain/install:libunwind",
|
||||
]:
|
||||
continue
|
||||
missing_deps, ambiguous = get_missing_deps(
|
||||
header_to_rule_map, generated_files, rule
|
||||
)
|
||||
|
||||
@@ -18,7 +18,7 @@ $(git diff --summary "${COMMIT_SHA}^!")
|
||||
|
||||
# Setup global git configuration.
|
||||
GIT_USERNAME="CarbonInfraBot"
|
||||
git config --global user.email "carbon-external-infra@google.com"
|
||||
git config --global user.email "infra-role@carbon-lang.dev"
|
||||
git config --global user.name "$GIT_USERNAME"
|
||||
|
||||
declare -A MIRRORS
|
||||
|
||||
@@ -137,3 +137,26 @@ cc_test(
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "unified_diff_matcher",
|
||||
testonly = 1,
|
||||
hdrs = ["unified_diff_matcher.h"],
|
||||
deps = [
|
||||
"//common:check",
|
||||
"@googletest//:gtest",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "unified_diff_matcher_test",
|
||||
size = "small",
|
||||
srcs = ["unified_diff_matcher_test.cpp"],
|
||||
deps = [
|
||||
":gtest_main",
|
||||
":unified_diff_matcher",
|
||||
"@googletest//:gtest",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
// 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
|
||||
|
||||
#ifndef CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_
|
||||
#define CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Sequence.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
namespace Carbon::Testing {
|
||||
|
||||
// Matcher that compares the elements of two containers and produces a unified
|
||||
// diff on failure.
|
||||
template <typename Container>
|
||||
class UnifiedDiffMatcher {
|
||||
public:
|
||||
explicit UnifiedDiffMatcher(Container expected)
|
||||
: expected_(std::move(expected)) {}
|
||||
|
||||
// Matches `actual` against `expected_`. Returns true on a match; returns
|
||||
// false and prints a unified diff to `listener` on a mismatch.
|
||||
template <typename ActualContainer>
|
||||
auto MatchAndExplain(const ActualContainer& actual,
|
||||
testing::MatchResultListener* listener) const -> bool;
|
||||
|
||||
auto DescribeTo(std::ostream* os) const -> void {
|
||||
*os << "matches elements with unified diff";
|
||||
}
|
||||
|
||||
auto DescribeNegationTo(std::ostream* os) const -> void {
|
||||
*os << "does not match elements with unified diff";
|
||||
}
|
||||
|
||||
private:
|
||||
// A 2D array, stored contiguously. Rows correspond to `expected_`'s elements,
|
||||
// and columns correspond to the actual container's elements.
|
||||
template <typename T>
|
||||
class Table;
|
||||
|
||||
// The result of a `Matches` check between an expected and actual element.
|
||||
enum class MatchResult : uint8_t { Unknown, Matches, DoesNotMatch };
|
||||
|
||||
// Checks whether `actual_element` matches `expected_[expected_index]`. It
|
||||
// first checks whether a cached result exists. If not, it evaluates the
|
||||
// match and stores the result in `match_results`.
|
||||
template <typename ActualElement>
|
||||
auto IsElementMatch(size_t expected_index, size_t actual_index,
|
||||
const ActualElement& actual_element,
|
||||
Table<MatchResult>& match_results) const -> bool {
|
||||
MatchResult cached_result = match_results.Get(expected_index, actual_index);
|
||||
if (cached_result != MatchResult::Unknown) {
|
||||
return cached_result == MatchResult::Matches;
|
||||
}
|
||||
bool is_match =
|
||||
testing::MatcherCast<const ActualElement&>(expected_[expected_index])
|
||||
.Matches(actual_element);
|
||||
match_results.Set(
|
||||
expected_index, actual_index,
|
||||
is_match ? MatchResult::Matches : MatchResult::DoesNotMatch);
|
||||
return is_match;
|
||||
}
|
||||
|
||||
// Returns true if every element in `expected_` matches the corresponding
|
||||
// element in `actual`. Stores comparisons in `match_results`.
|
||||
template <typename ActualContainer>
|
||||
auto IsEqual(const ActualContainer& actual,
|
||||
Table<MatchResult>& match_results) const -> bool;
|
||||
|
||||
// Populates `subsequences` with the longest common matching subsequences
|
||||
// found when comparing `actual` and `expected_`. Stores comparisons in
|
||||
// `match_results`.
|
||||
template <typename ActualContainer>
|
||||
auto GetLongestCommonSubsequences(const ActualContainer& actual,
|
||||
Table<MatchResult>& match_results,
|
||||
Table<int>& subsequences) const -> void;
|
||||
|
||||
// Prints the unified diff.
|
||||
template <typename ActualContainer>
|
||||
auto PrintDiff(const ActualContainer& actual,
|
||||
Table<MatchResult>& match_results,
|
||||
const Table<int>& subsequences,
|
||||
testing::MatchResultListener* listener) const -> void;
|
||||
|
||||
// The expected elements.
|
||||
Container expected_;
|
||||
};
|
||||
|
||||
// Returns a polymorphic matcher that acts similarly to
|
||||
// ElementsAreArray but produces a unified diff on failure.
|
||||
template <typename Container>
|
||||
auto ElementsAreArrayWithUnifiedDiff(Container expected) {
|
||||
return testing::MakePolymorphicMatcher(
|
||||
UnifiedDiffMatcher<Container>(std::move(expected)));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Internal implementation details follow.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
template <typename Container>
|
||||
template <typename T>
|
||||
class UnifiedDiffMatcher<Container>::Table {
|
||||
public:
|
||||
// Constructs a table with dimensions of expected_size and actual_size,
|
||||
// corresponding to the containers being compared.
|
||||
Table(int expected_size, int actual_size, T default_value)
|
||||
: actual_size_(actual_size),
|
||||
data_(expected_size * actual_size, default_value) {}
|
||||
|
||||
// Sets the value at the given expected_index and actual_index.
|
||||
auto Set(int expected_index, int actual_index, T value) -> void {
|
||||
data_[expected_index * actual_size_ + actual_index] = std::move(value);
|
||||
}
|
||||
|
||||
// Gets the value at the given expected_index and actual_index.
|
||||
auto Get(int expected_index, int actual_index) const -> T {
|
||||
return data_[expected_index * actual_size_ + actual_index];
|
||||
}
|
||||
|
||||
private:
|
||||
// The actual_size of the table.
|
||||
int actual_size_;
|
||||
// The contiguous data storage for the table.
|
||||
llvm::SmallVector<T> data_;
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::MatchAndExplain(
|
||||
const ActualContainer& actual, testing::MatchResultListener* listener) const
|
||||
-> bool {
|
||||
Table<MatchResult> match_results(expected_.size(), std::size(actual),
|
||||
MatchResult::Unknown);
|
||||
|
||||
if (IsEqual(actual, match_results)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (listener->IsInterested()) {
|
||||
Table<int> subsequences(expected_.size() + 1, std::size(actual) + 1, 0);
|
||||
GetLongestCommonSubsequences(actual, match_results, subsequences);
|
||||
PrintDiff(actual, match_results, subsequences, listener);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::IsEqual(
|
||||
const ActualContainer& actual, Table<MatchResult>& match_results) const
|
||||
-> bool {
|
||||
if (expected_.size() != std::size(actual)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto [i, actual_element] : llvm::enumerate(actual)) {
|
||||
if (!IsElementMatch(i, i, actual_element, match_results)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::GetLongestCommonSubsequences(
|
||||
const ActualContainer& actual, Table<MatchResult>& match_results,
|
||||
Table<int>& subsequences) const -> void {
|
||||
for (auto expected_index : llvm::seq(expected_.size())) {
|
||||
for (auto [actual_index, actual_element] : llvm::enumerate(actual)) {
|
||||
int subsequence_value;
|
||||
if (IsElementMatch(expected_index, actual_index, actual_element,
|
||||
match_results)) {
|
||||
// If the elements match, the LCS length increases by 1 relative to
|
||||
// the prefixes where both elements are excluded.
|
||||
subsequence_value = subsequences.Get(expected_index, actual_index) + 1;
|
||||
} else {
|
||||
// Otherwise, the LCS length is the maximum of the LCS lengths
|
||||
// relative to the prefixes where one element is excluded.
|
||||
subsequence_value =
|
||||
std::max(subsequences.Get(expected_index, actual_index + 1),
|
||||
subsequences.Get(expected_index + 1, actual_index));
|
||||
}
|
||||
subsequences.Set(expected_index + 1, actual_index + 1, subsequence_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
template <typename ActualContainer>
|
||||
auto UnifiedDiffMatcher<Container>::PrintDiff(
|
||||
const ActualContainer& actual, Table<MatchResult>& match_results,
|
||||
const Table<int>& subsequences,
|
||||
testing::MatchResultListener* listener) const -> void {
|
||||
// A line in the diff output.
|
||||
struct DiffLine {
|
||||
enum class Kind { Match, ActualOnly, ExpectedOnly };
|
||||
Kind kind;
|
||||
// Only used for `Match` and `ActualOnly`.
|
||||
const ActualContainer::value_type* actual_value;
|
||||
int expected_index;
|
||||
};
|
||||
|
||||
llvm::SmallVector<DiffLine> diff;
|
||||
// Reserve a quick upper bound of the size.
|
||||
diff.reserve(expected_.size() + std::size(actual));
|
||||
|
||||
// Reconstruct the diff by backtracking from the end of the table.
|
||||
int expected_index = expected_.size() - 1;
|
||||
int actual_index = std::size(actual) - 1;
|
||||
auto actual_it = std::end(actual) - 1;
|
||||
while (expected_index >= 0 || actual_index >= 0) {
|
||||
auto match_result = (expected_index >= 0 && actual_index >= 0)
|
||||
? match_results.Get(expected_index, actual_index)
|
||||
: MatchResult::DoesNotMatch;
|
||||
CARBON_CHECK(match_result != MatchResult::Unknown);
|
||||
if (match_result == MatchResult::Matches) {
|
||||
// The element is in both lists for the diff.
|
||||
diff.push_back({.kind = DiffLine::Kind::Match,
|
||||
.actual_value = &*actual_it,
|
||||
.expected_index = expected_index});
|
||||
--expected_index;
|
||||
--actual_index;
|
||||
--actual_it;
|
||||
} else if (actual_index >= 0 &&
|
||||
(expected_index < 0 ||
|
||||
subsequences.Get(expected_index + 1, actual_index) >=
|
||||
subsequences.Get(expected_index, actual_index + 1))) {
|
||||
// Dropping an element from `actual` preserves the LCS length, so treat it
|
||||
// as an insertion.
|
||||
diff.push_back({.kind = DiffLine::Kind::ActualOnly,
|
||||
.actual_value = &*actual_it,
|
||||
.expected_index = std::max(0, expected_index)});
|
||||
--actual_index;
|
||||
--actual_it;
|
||||
} else {
|
||||
// Otherwise, treat it as a deletion from `expected`.
|
||||
diff.push_back({.kind = DiffLine::Kind::ExpectedOnly,
|
||||
.actual_value = nullptr,
|
||||
.expected_index = expected_index});
|
||||
--expected_index;
|
||||
}
|
||||
}
|
||||
|
||||
struct PrintRange {
|
||||
int begin;
|
||||
int end;
|
||||
};
|
||||
llvm::SmallVector<PrintRange> print_ranges;
|
||||
|
||||
constexpr int ContextLines = 3;
|
||||
for (auto [i, line] :
|
||||
llvm::reverse(llvm::zip_equal(llvm::seq<int>(diff.size()), diff))) {
|
||||
if (line.kind != DiffLine::Kind::Match) {
|
||||
PrintRange range = {
|
||||
.begin = std::max(0, i - ContextLines),
|
||||
.end = std::min<int>(diff.size() - 1, i + ContextLines)};
|
||||
if (print_ranges.empty() || print_ranges.back().begin > range.end + 1) {
|
||||
print_ranges.push_back(range);
|
||||
} else {
|
||||
// Merge diffs with overlapping context.
|
||||
print_ranges.back().begin = range.begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*listener << "unified diff (- expected, + actual):\n";
|
||||
for (const auto& range : print_ranges) {
|
||||
*listener << "=== diff in expected elements "
|
||||
<< diff[range.end].expected_index + 1 << " to "
|
||||
<< diff[range.begin].expected_index + 1 << " (1-based index):\n";
|
||||
for (auto i : llvm::reverse(llvm::seq_inclusive(range.begin, range.end))) {
|
||||
const auto& line = diff[i];
|
||||
if (line.kind == DiffLine::Kind::Match) {
|
||||
*listener << " " << *line.actual_value << "\n";
|
||||
} else if (line.kind == DiffLine::Kind::ActualOnly) {
|
||||
*listener << "+ " << *line.actual_value << "\n";
|
||||
} else {
|
||||
*listener << "- ";
|
||||
expected_[line.expected_index].DescribeTo(listener->stream());
|
||||
*listener << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
*listener << "=== diff end\n";
|
||||
}
|
||||
|
||||
} // namespace Carbon::Testing
|
||||
|
||||
#endif // CARBON_TESTING_BASE_UNIFIED_DIFF_MATCHER_H_
|
||||
@@ -0,0 +1,247 @@
|
||||
// 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
|
||||
|
||||
#include "testing/base/unified_diff_matcher.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
namespace Carbon::Testing {
|
||||
namespace {
|
||||
|
||||
using ::testing::Matcher;
|
||||
using ::testing::StrEq;
|
||||
|
||||
// Asserts that when expected does not match actual, the string
|
||||
// representation of the produced diff equals expected_diff.
|
||||
auto ExpectUnifiedDiff(const llvm::SmallVector<std::string>& actual,
|
||||
const llvm::SmallVector<Matcher<std::string>>& expected,
|
||||
const std::string& expected_diff) -> void {
|
||||
testing::StringMatchResultListener listener;
|
||||
EXPECT_FALSE(testing::ExplainMatchResult(
|
||||
ElementsAreArrayWithUnifiedDiff(expected), actual, &listener));
|
||||
EXPECT_THAT(listener.str(), testing::Eq(expected_diff));
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, Matches) {
|
||||
llvm::SmallVector<std::string> actual = {"A", "B", "C"};
|
||||
llvm::SmallVector<Matcher<std::string>> expected = {StrEq("A"), StrEq("B"),
|
||||
StrEq("C")};
|
||||
EXPECT_THAT(actual, ElementsAreArrayWithUnifiedDiff(expected));
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchMissing) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 3 (1-based index):
|
||||
A
|
||||
- is equal to "B"
|
||||
C
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"A", "C"}, {StrEq("A"), StrEq("B"), StrEq("C")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchExtra) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 2 (1-based index):
|
||||
A
|
||||
+ B
|
||||
C
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"A", "B", "C"}, {StrEq("A"), StrEq("C")}, ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchBoth) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 2 (1-based index):
|
||||
A
|
||||
- is equal to "C"
|
||||
+ B
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"A", "B"}, {StrEq("A"), StrEq("C")}, ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchMultiple) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 5 (1-based index):
|
||||
A
|
||||
- is equal to "B"
|
||||
+ X
|
||||
C
|
||||
- is equal to "D"
|
||||
+ Y
|
||||
E
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff(
|
||||
{"A", "X", "C", "Y", "E"},
|
||||
{StrEq("A"), StrEq("B"), StrEq("C"), StrEq("D"), StrEq("E")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchLongContext) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 2 to 8 (1-based index):
|
||||
1
|
||||
2
|
||||
3
|
||||
- is equal to "X"
|
||||
+ 4
|
||||
5
|
||||
6
|
||||
7
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7", "8"},
|
||||
{StrEq("0"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("X"),
|
||||
StrEq("5"), StrEq("6"), StrEq("7"), StrEq("8")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, Mismatch5LineContext) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 7 (1-based index):
|
||||
- is equal to "X"
|
||||
+ 0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
- is equal to "Y"
|
||||
+ 6
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6"},
|
||||
{StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"),
|
||||
StrEq("5"), StrEq("Y")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, Mismatch6LineContext) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 8 (1-based index):
|
||||
- is equal to "X"
|
||||
+ 0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
- is equal to "Y"
|
||||
+ 7
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7"},
|
||||
{StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"),
|
||||
StrEq("5"), StrEq("6"), StrEq("Y")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, Mismatch7LineContext) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 4 (1-based index):
|
||||
- is equal to "X"
|
||||
+ 0
|
||||
1
|
||||
2
|
||||
3
|
||||
=== diff in expected elements 6 to 9 (1-based index):
|
||||
5
|
||||
6
|
||||
7
|
||||
- is equal to "Y"
|
||||
+ 8
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"0", "1", "2", "3", "4", "5", "6", "7", "8"},
|
||||
{StrEq("X"), StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"),
|
||||
StrEq("5"), StrEq("6"), StrEq("7"), StrEq("Y")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchEmptyExpected) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 1 (1-based index):
|
||||
+ A
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"A"}, {}, ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchEmptyActual) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 1 (1-based index):
|
||||
- is equal to "A"
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({}, {StrEq("A")}, ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchLongDifference) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 4 (1-based index):
|
||||
1
|
||||
- is equal to "2"
|
||||
- is equal to "3"
|
||||
+ X
|
||||
+ Y
|
||||
+ Z
|
||||
4
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"1", "X", "Y", "Z", "4"},
|
||||
{StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncActualMissing) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 6 (1-based index):
|
||||
1
|
||||
2
|
||||
- is equal to "3"
|
||||
+ X
|
||||
+ 7
|
||||
4
|
||||
5
|
||||
6
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff({"1", "2", "X", "7", "4", "5", "6", "7", "8", "9"},
|
||||
{StrEq("1"), StrEq("2"), StrEq("3"), StrEq("4"), StrEq("5"),
|
||||
StrEq("6"), StrEq("7"), StrEq("8"), StrEq("9")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
TEST(UnifiedDiffMatcherTest, MismatchGreedyResyncExpectedMissing) {
|
||||
constexpr char ExpectedDiff[] = R"(unified diff (- expected, + actual):
|
||||
=== diff in expected elements 1 to 7 (1-based index):
|
||||
1
|
||||
2
|
||||
- is equal to "X"
|
||||
- is equal to "7"
|
||||
+ 3
|
||||
4
|
||||
5
|
||||
6
|
||||
=== diff end
|
||||
)";
|
||||
ExpectUnifiedDiff(
|
||||
{"1", "2", "3", "4", "5", "6", "7", "8", "9"},
|
||||
{StrEq("1"), StrEq("2"), StrEq("X"), StrEq("7"), StrEq("4"), StrEq("5"),
|
||||
StrEq("6"), StrEq("7"), StrEq("8"), StrEq("9")},
|
||||
ExpectedDiff);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Carbon::Testing
|
||||
@@ -54,6 +54,7 @@ cc_library(
|
||||
"//common:raw_string_ostream",
|
||||
"//common:set",
|
||||
"//testing/base:file_helpers",
|
||||
"//testing/base:unified_diff_matcher",
|
||||
"@abseil-cpp//absl/flags:flag",
|
||||
"@abseil-cpp//absl/flags:parse",
|
||||
"@abseil-cpp//absl/strings",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "llvm/Support/PrettyStackTrace.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/ThreadPool.h"
|
||||
#include "testing/base/unified_diff_matcher.h"
|
||||
#include "testing/file_test/autoupdate.h"
|
||||
#include "testing/file_test/run_test.h"
|
||||
#include "testing/file_test/test_file.h"
|
||||
@@ -119,7 +120,6 @@ static auto SplitOutput(llvm::StringRef output)
|
||||
llvm::StringRef(output).split(lines, "\n");
|
||||
return llvm::SmallVector<std::string_view>(lines.begin(), lines.end());
|
||||
}
|
||||
|
||||
// Verify that the success and `fail_` prefix use correspond. Separately handle
|
||||
// both cases for clearer test failures.
|
||||
static auto CompareFailPrefix(llvm::StringRef filename, bool success) -> void {
|
||||
@@ -259,13 +259,9 @@ auto FileTestCase::TestBody() -> void {
|
||||
|
||||
} else {
|
||||
EXPECT_THAT(SplitOutput(test_file.actual_stdout),
|
||||
ElementsAreArray(test_file.expected_stdout))
|
||||
<< "Actual text:\n"
|
||||
<< test_file.actual_stdout;
|
||||
ElementsAreArrayWithUnifiedDiff(test_file.expected_stdout));
|
||||
EXPECT_THAT(SplitOutput(test_file.actual_stderr),
|
||||
ElementsAreArray(test_file.expected_stderr))
|
||||
<< "Actual text:\n"
|
||||
<< test_file.actual_stderr;
|
||||
ElementsAreArrayWithUnifiedDiff(test_file.expected_stderr));
|
||||
}
|
||||
|
||||
if (HasFailure()) {
|
||||
|
||||
@@ -104,10 +104,10 @@ static auto CollectOutputIfCapturing(TestFile& test_file) -> void {
|
||||
auto RunTestFile(const FileTestBase& test_base, bool dump_output,
|
||||
TestFile& test_file) -> ErrorOr<Success> {
|
||||
llvm::SmallVector<TestFile::Split*> all_splits;
|
||||
for (auto& split : test_file.file_splits) {
|
||||
for (auto& split : test_file.include_file_splits) {
|
||||
all_splits.push_back(&split);
|
||||
}
|
||||
for (auto& split : test_file.include_file_splits) {
|
||||
for (auto& split : test_file.file_splits) {
|
||||
all_splits.push_back(&split);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,4 +10,4 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_empty.carbon
|
||||
|
||||
// CHECK:STDOUT: 3 args: `default_args`, `include_empty.carbon`, `empty.carbon`
|
||||
// CHECK:STDOUT: 3 args: `default_args`, `empty.carbon`, `include_empty.carbon`
|
||||
|
||||
+1
-1
@@ -11,4 +11,4 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_extra_args.carbon
|
||||
|
||||
// CHECK:STDOUT: 6 args: `default_args`, `include_extra_args.carbon`, `include_files/extra_args.carbon`, `bar`, `baz`, `foo`
|
||||
// CHECK:STDOUT: 6 args: `default_args`, `include_files/extra_args.carbon`, `include_extra_args.carbon`, `bar`, `baz`, `foo`
|
||||
|
||||
+1
-1
@@ -10,5 +10,5 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_no_split.carbon
|
||||
|
||||
// CHECK:STDOUT: 3 args: `default_args`, `include_no_split.carbon`, `include_files/no_split.carbon`
|
||||
// CHECK:STDOUT: 3 args: `default_args`, `include_files/no_split.carbon`, `include_no_split.carbon`
|
||||
// CHECK:STDOUT: include_files/no_split.carbon:5: no split
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_recursive.carbon
|
||||
|
||||
// CHECK:STDOUT: 6 args: `default_args`, `include_recursive.carbon`, `c.carbon`, `d.carbon`, `a.carbon`, `b.carbon`
|
||||
// CHECK:STDOUT: 6 args: `default_args`, `c.carbon`, `d.carbon`, `a.carbon`, `b.carbon`, `include_recursive.carbon`
|
||||
// CHECK:STDOUT: c.carbon:2: c
|
||||
// CHECK:STDOUT: d.carbon:2: d
|
||||
// CHECK:STDOUT: a.carbon:2: a
|
||||
|
||||
+1
-1
@@ -12,5 +12,5 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_repeated.carbon
|
||||
|
||||
// CHECK:STDOUT: 3 args: `default_args`, `include_repeated.carbon`, `include_files/no_split.carbon`
|
||||
// CHECK:STDOUT: 3 args: `default_args`, `include_files/no_split.carbon`, `include_repeated.carbon`
|
||||
// CHECK:STDOUT: include_files/no_split.carbon:5: no split
|
||||
|
||||
+1
-1
@@ -10,6 +10,6 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //testing/file_test:file_test_base_test -- --dump_output --file_tests=testing/file_test/testdata/include_split.carbon
|
||||
|
||||
// CHECK:STDOUT: 4 args: `default_args`, `include_split.carbon`, `a.carbon`, `b.carbon`
|
||||
// CHECK:STDOUT: 4 args: `default_args`, `a.carbon`, `b.carbon`, `include_split.carbon`
|
||||
// CHECK:STDOUT: a.carbon:2: a
|
||||
// CHECK:STDOUT: b.carbon:2: b
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ run_tool(
|
||||
name = "carbon",
|
||||
data = ["//toolchain/install:install_data"],
|
||||
env = cc_env(),
|
||||
tool = "//toolchain/install:prefix/bin/carbon",
|
||||
tool = "//toolchain/install:carbon-busybox",
|
||||
)
|
||||
|
||||
# A convenience target for running the toolchain with the full prelude
|
||||
|
||||
@@ -8,6 +8,10 @@ load("runtimes_build_info.bzl", "generate_runtimes_build_info_cc_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"runtimes_build_vars.tpl.bzl",
|
||||
])
|
||||
|
||||
cc_library(
|
||||
name = "block_value_store",
|
||||
hdrs = ["block_value_store.h"],
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
namespace Carbon {
|
||||
|
||||
// The location within our Bazel output tree of the install root.
|
||||
static constexpr llvm::StringLiteral BazelRoot =
|
||||
"carbon/toolchain/install/prefix/lib/carbon/";
|
||||
static constexpr llvm::StringLiteral BazelRoot = "carbon/toolchain/install/";
|
||||
|
||||
// Path within an install root for our marker of a valid install.
|
||||
static constexpr llvm::StringLiteral MarkerPath = "carbon_install.txt";
|
||||
@@ -84,7 +83,7 @@ auto InstallPaths::ReadClangHeadersManifest() const
|
||||
// root. Consider whether this manifest should be within the install or
|
||||
// consider moving the code to access it to be separate and specific to the
|
||||
// infrastructure needing it.
|
||||
return ReadManifest(root_ / "../../..", "clang_headers_manifest.txt");
|
||||
return ReadManifest(root_, "clang_headers_manifest.txt");
|
||||
}
|
||||
|
||||
auto InstallPaths::ReadManifest(std::filesystem::path manifest_path,
|
||||
|
||||
@@ -148,7 +148,7 @@ class InstallPaths {
|
||||
auto SetError(llvm::Twine message) -> void;
|
||||
|
||||
// Check that the install paths have a marker file at
|
||||
// `root()/lib/carbon/carbon_install.txt". If not, calls `SetError` with the
|
||||
// `root()/carbon_install.txt". If not, calls `SetError` with the
|
||||
// relevant error message.
|
||||
auto CheckMarkerFile() -> void;
|
||||
|
||||
@@ -162,7 +162,7 @@ class InstallPaths {
|
||||
//
|
||||
// When run from Bazel (for example, in unit tests or development binaries)
|
||||
// this will look like:
|
||||
// `bazel-bin/some/bazel/target.runfiles/_main/toolchain/install/prefix/lib/carbon`
|
||||
// `bazel-bin/some/bazel/target.runfiles/_main/toolchain/install`
|
||||
//
|
||||
// When installed, it's expected to be similar to the CMake install prefix,
|
||||
// followed by `lib/carbon`:
|
||||
@@ -177,7 +177,7 @@ class InstallPaths {
|
||||
// our installation to behave in a similar and compatible way.
|
||||
//
|
||||
// The hierarchy of files beneath the install root can be found in the
|
||||
// BUILD's `install_dirs` entry for `lib/carbon`.
|
||||
// BUILD rules in `//toolchain/install`.
|
||||
std::filesystem::path root_;
|
||||
|
||||
// The opened root directory.
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace {
|
||||
|
||||
using ::bazel::tools::cpp::runfiles::Runfiles;
|
||||
using ::testing::_;
|
||||
using ::testing::EndsWith;
|
||||
using ::testing::Eq;
|
||||
using ::testing::HasSubstr;
|
||||
using Testing::IsSuccess;
|
||||
@@ -61,14 +60,6 @@ class InstallPathsTest : public ::testing::Test {
|
||||
ASSERT_THAT(root_result, IsSuccess(_));
|
||||
Filesystem::Dir root = *std::move(root_result);
|
||||
|
||||
// Check that the root is located in the expected part of the FHS layout.
|
||||
// TODO: Adjust this to work equally well on Windows.
|
||||
EXPECT_THAT(root_path.native(), EndsWith("lib/carbon/"));
|
||||
EXPECT_THAT(
|
||||
root.Access("../../bin/carbon", Filesystem::AccessCheckFlags::Execute),
|
||||
IsSuccess(Eq(true)))
|
||||
<< "path: " << (root_path / "../../bin/carbon");
|
||||
|
||||
std::filesystem::path core_package_path = paths.core_package();
|
||||
ASSERT_THAT(core_package_path, StartsWith(root_path));
|
||||
EXPECT_THAT(Filesystem::Cwd().Access(core_package_path / "prelude.carbon"),
|
||||
@@ -93,8 +84,8 @@ class InstallPathsTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(InstallPathsTest, RootBusybox) {
|
||||
std::string installed_busybox_path = test_runfiles_->Rlocation(
|
||||
"carbon/toolchain/install/prefix/lib/carbon/carbon-busybox");
|
||||
std::string installed_busybox_path =
|
||||
test_runfiles_->Rlocation("carbon/toolchain/install/carbon-busybox");
|
||||
|
||||
auto paths = InstallPaths::MakeExeRelative(installed_busybox_path);
|
||||
ASSERT_THAT(paths.error(), Eq(std::nullopt)) << *paths.error();
|
||||
@@ -102,8 +93,8 @@ TEST_F(InstallPathsTest, RootBusybox) {
|
||||
}
|
||||
|
||||
TEST_F(InstallPathsTest, RootExplicit) {
|
||||
std::string marker_path = test_runfiles_->Rlocation(
|
||||
"carbon/toolchain/install/prefix/lib/carbon/carbon_install.txt");
|
||||
std::string marker_path =
|
||||
test_runfiles_->Rlocation("carbon/toolchain/install/carbon_install.txt");
|
||||
|
||||
llvm::StringRef root_path = marker_path;
|
||||
CARBON_CHECK(root_path.consume_back("carbon_install.txt"),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user