Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for entity declaration design work #4230

Merged
merged 6 commits into from
Aug 21, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 233 additions & 0 deletions docs/design/declaring_entities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# Declaring entities

<!--
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
-->

<!-- toc -->

## Table of contents

- [Overview](#overview)
- [Matching redeclarations of an entity](#matching-redeclarations-of-an-entity)
- [Details](#details)
- [`extern` and `extern library`](#extern-and-extern-library)
- [Effect on indirect imports](#effect-on-indirect-imports)
- [Alternatives considered](#alternatives-considered)
- [References](#references)

<!-- tocstop -->

## Overview

Entities may have up to three declarations:

- An optional, owning forward declaration.
- For example, `class MyClass;`.
- This must come before the definition. The API file is considered to be
before the implementation file.
- A required, owning definition.
- For example, `class MyClass { ... }`.
- The definition might be the _only_ declaration.
- An optional, non-owning `extern library "<owning_library>"` declaration.
- For example, `extern library "OtherLibrary" class MyClass;`.
- It must be in a separate library from the definition.
- The owning library's API file must import the `extern` declaration, and
must also contain a declaration.
- The owning library's declarations must have the `extern` modifier
(without `library`).
- For example, `extern class MyClass;`.

For example, a library can have a forward declaration of an entity in the API
file, and use the implementation file for the entity's definition. Putting the
definition in an implementation file this way can reduce the dependencies for
API file evaluation, improving compile time. This is commonly done with
functions. For example:

```
library "MyLibrary";

fn DoSomething();
```

```
impl library "MyLibrary";

fn DoSomething {
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
...
}
```

## Matching redeclarations of an entity

In order to determine whether two redeclarations refer to the same entity, we
apply the rules:

- Two named declarations _declare the same entity_ if they have the same scope
and the same name.
- One declaration _redeclares_ another if they declare the same entity and the
second declaration appears after the first, including the case where the
first declaration is imported. In this case, the second declaration is said
to be a _redeclaration_ of the first.
josh11b marked this conversation as resolved.
Show resolved Hide resolved
- Two declarations _differ_ if the sequence of tokens in the declaration
following the introducer keyword and the optional scope, up to the semicolon
or open brace, is different, except for `unused` modifiers on parameters.
- The program is invalid if it contains two owning declarations of the same
entity that differ.
- The non-owned `extern library` declarations will only use semantic
matching for redeclarations, not syntactic matching.

```carbon
class A {
fn F(n: i32);
fn G(n: i32);
}

// ❌ Invalid: The parameter name differs.
fn A.F(m: i32) {}

// ❌ Invalid: The parameter type differs syntactically.
fn A.G(n: (i32)) {}
josh11b marked this conversation as resolved.
Show resolved Hide resolved
```

### Details

TODO: Figure out what details to pull from
[#3762](https://github.com/carbon-language/carbon-lang/pull/3763) and
[#3763](https://github.com/carbon-language/carbon-lang/pull/3763).

## `extern` and `extern library`

There are two forms of the `extern` modifier:

- On an owning declaration, `extern` limits access to the definition.
- The entity must be directly imported in order to use of the definition.
- An `extern library` declaration is optional.
- On a non-owning declaration, `extern library` provides a tool for breaking
dependency cycles.
josh11b marked this conversation as resolved.
Show resolved Hide resolved
- The library name indicates where the entity is defined.
-
josh11b marked this conversation as resolved.
Show resolved Hide resolved

For example, a use of both might look like:

```
library "owner";

// This `import` is required due to the `extern library`, but we also make use
// of `MyClassFactory` below. This is a circular use of `MyClass` that we
// couldn't split between libraries without `extern`.
import library "factory";

extern class MyClass {
fn Make() -> MyClass* {
return MyClassFactory();
}

var val: i32 = 0;
}
```

```
library "factory";

// Declares `MyClass` so that `MyClassFactory` can return it.
extern library "owner" class MyClass;

fn MyClassFactory(val: i32) -> MyClass*;
```

```
impl library "factory";

// Imports the definition of `MyClass`.
import library "owner";

extern fn MyClassFactory(val: i32) -> MyClass* {
var c: MyClass* = new MyClass();
c->val = val;
return c;
}
```

### Effect on indirect imports

Indirect imports won't see the definition of an entity. We expect this to
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
primarily effect return types of functions. If an incomplete type is encountered
jonmeow marked this conversation as resolved.
Show resolved Hide resolved
this way, it can be resolved by directly importing the definition. For example:

```
library "type";

// Because this is `extern`, the definition must be directly imported.
extern class MyType { var x: i32 }
```

```
library "make_type";

import library "type";

// Here we have a function which returns the type.
fn MakeMyType() -> MyType*;
```

```
library "invalid_use";

import library "make_type";

fn InvalidUse() -> i32 {
// ❌ Invalid: `MyType` is incomplete because it's `extern` and not directly
// imported. `x` cannot be accessed.
return MakeMyType()->x;
}
```

```
library "valid_use";

import library "make_type";

// ✅ Valid: By directly importing the definition, we can now access `x`.
import library "type";

fn ValidUse() -> i32 {
return MakeMyType()->x;
}
```

## Alternatives considered

- [Other modifier keyword merging approaches](/proposals/p3762.md#other-modifier-keyword-merging-approaches)
- [No `extern` keyword](/proposals/p3762.md#no-extern-keyword)
- [Looser restrictions on declarations](/proposals/p3762.md#looser-restrictions-on-declarations)
- [`extern` naming](/proposals/p3762.md#extern-naming)
- [Default `extern` to private](/proposals/p3762.md#default-extern-to-private)
- [Opaque types](/proposals/p3762.md#opaque-types)
- [Require a library provide its own `extern` declarations](/proposals/p3762.md#require-a-library-provide-its-own-extern-declarations)
- [Allow cross-package `extern` declarations](/proposals/p3762.md#allow-cross-package-extern-declarations)
- [Use a partially or fully semantic rule](/proposals/p3763.md#use-a-partially-or-fully-semantic-rule)
- [Use package-wide name poisoning](/proposals/p3763.md#use-package-wide-name-poisoning)
- [Allow shadowing in implementation file after use in API file](/proposals/p3763.md#allow-shadowing-in-implementation-file-after-use-in-api-file)
- [Allow multiple non-owning declarations, remove the import requirement, or both](/proposals/p3980.md#allow-multiple-non-owning-declarations-remove-the-import-requirement-or-both)
- [Total number of allowed declarations (owning and non-owning)](/proposals/p3980.md#total-number-of-allowed-declarations-owning-and-non-owning)
- [Do not restrict the number of forward declarations](/proposals/p3980.md#do-not-restrict-the-number-of-forward-declarations)
- [Allow up to two declarations total](/proposals/p3980.md#allow-up-to-two-declarations-total)
- [Allow up to four declarations total](/proposals/p3980.md#allow-up-to-four-declarations-total)
- [Don't require a modifier on the owning declarations](/proposals/p3980.md#dont-require-a-modifier-on-the-owning-declarations)
- [Only require `extern` on the first owning declaration](/proposals/p3980.md#only-require-extern-on-the-first-owning-declaration)
- [Separate require-direct-import from non-owning declarations](/proposals/p3980.md#separate-require-direct-import-from-non-owning-declarations)
- [Other `extern` syntaxes](/proposals/p3980.md#other-extern-syntaxes)
- [Have types with `extern` members re-export them](/proposals/p3980.md#have-types-with-extern-members-re-export-them)
- [Require syntactic matching for `extern library` declarations](/proposals/p3980.md#require-syntactic-matching-for-extern-library-declarations)

## References

- Proposal
[#3762: Merging forward declarations](https://github.com/carbon-language/carbon-lang/pull/3762)
- Proposal
[#3763: Matching redeclarations](https://github.com/carbon-language/carbon-lang/pull/3763)
- Proposal
[#3980: Singular `extern` declarations](https://github.com/carbon-language/carbon-lang/pull/3980)
Loading