diff options
Diffstat (limited to 'docs/proposals')
| -rw-r--r-- | docs/proposals/000-template.md | 2 | ||||
| -rw-r--r-- | docs/proposals/001-where-clauses.md | 10 | ||||
| -rw-r--r-- | docs/proposals/002-type-equality-constraints.md | 6 | ||||
| -rw-r--r-- | docs/proposals/003-atomic-t.md | 2 | ||||
| -rw-r--r-- | docs/proposals/004-initialization.md | 16 | ||||
| -rw-r--r-- | docs/proposals/005-write-only-textures.md | 2 | ||||
| -rw-r--r-- | docs/proposals/007-variadic-generics.md | 10 | ||||
| -rw-r--r-- | docs/proposals/012-language-version-directive.md | 8 | ||||
| -rw-r--r-- | docs/proposals/implementation/ast-ir-serialization.md | 14 | ||||
| -rw-r--r-- | docs/proposals/legacy/001-basic-interfaces.md | 2 | ||||
| -rw-r--r-- | docs/proposals/legacy/002-api-headers.md | 28 | ||||
| -rw-r--r-- | docs/proposals/legacy/003-error-handling.md | 8 | ||||
| -rw-r--r-- | docs/proposals/legacy/005-components.md | 14 | ||||
| -rw-r--r-- | docs/proposals/legacy/006-artifact-container-format.md | 4 |
14 files changed, 63 insertions, 63 deletions
diff --git a/docs/proposals/000-template.md b/docs/proposals/000-template.md index 3803d856b..cb0377886 100644 --- a/docs/proposals/000-template.md +++ b/docs/proposals/000-template.md @@ -13,7 +13,7 @@ Status Status: Design Review/Planned/Implementation In-Progress/Implemented/Partially Implemented. Note here whether the proposal is unimplemented, in-progress, has landed, etc. -Implemtation: [PR 000] [PR 001] ... (list links to PRs) +Implementation: [PR 000] [PR 001] ... (list links to PRs) Author: authors of the design doc and the implementation. diff --git a/docs/proposals/001-where-clauses.md b/docs/proposals/001-where-clauses.md index 0e49735e2..02f60a08f 100644 --- a/docs/proposals/001-where-clauses.md +++ b/docs/proposals/001-where-clauses.md @@ -81,7 +81,7 @@ C# is broadly similar, but uses multiple `where` clauses, one per constraint: While Haskell is a quite different language from the others mentioned here, Haskell typeclasses have undeniably influenced the concept of traits/protocols in Rust/Swift. -In Haskell a typeclass is not somethign a type "inherits" from, and instead uses type parameter for even the `This` type. +In Haskell a typeclass is not something a type "inherits" from, and instead uses type parameter for even the `This` type. Type parameters in Haskell are also introduced implicitly rather than explicitly. The `resolve` example above would become something like: @@ -89,7 +89,7 @@ The `resolve` example above would become something like: ResolutionContext u -> List t -> v We see here that the constraints are all grouped together in the `(...) =>` clause before the actual type signature of the function. -That clause serves a simlar semantic role to `where` clauses in these other languages. +That clause serves a similar semantic role to `where` clauses in these other languages. Proposed Approach ----------------- @@ -223,7 +223,7 @@ Technically it was already possible to have redundancy in a constraint by using void f<T : IBase & IDerived>( ... ) { ... } -One question that is raised by the possiblity of redundant constraints is whether the compiler should produce a diagnostic for them and, if so, whether it should be a warning or an error. +One question that is raised by the possibility of redundant constraints is whether the compiler should produce a diagnostic for them and, if so, whether it should be a warning or an error. While it may seem obvious that redundant constraints are to be avoided, it is possible that refactoring of `interface` hierarchies could change whether existing constraints are redundant or not, potentially forcing widespread edits to code that is semantically unambiguous (and just a little more verbose than necessary). We propose that redundant constraints should probably produce a warning, with a way to silence that warning easily. @@ -231,7 +231,7 @@ We propose that redundant constraints should probably produce a warning, with a The long and short of the above section is that there can be multiple ways to write semantically equivalent generic declarations, by changing the form, order, etc. of constraints. We want the signature of a function (and its mangled name, etc.) to be identical for semantically equivalent declaration syntax. -In order to ensure that a declaration's mangled name is indepenent of the form of its constraints, we must have a way to *canonicalize* those constraints. +In order to ensure that a declaration's mangled name is independent of the form of its constraints, we must have a way to *canonicalize* those constraints. The Swift compiler codebase includes a document that details the rules used for canonicalization of constraints for that compiler, and we can take inspiration from it. Our constraints are currently much more restricted, so canonicalization can follow a much simpler process, such as: @@ -288,7 +288,7 @@ In the context of `class`-based hierarchies, we can also consider having constra ### Allow `where` clauses on non-generic declarations -We could consider allowing `where` clauses to appear on any declaration nested under a generic, such that those declarations are only usable when certain additinal constraints are met. +We could consider allowing `where` clauses to appear on any declaration nested under a generic, such that those declarations are only usable when certain additional constraints are met. E.g.,: struct MyDictionary<K,V> diff --git a/docs/proposals/002-type-equality-constraints.md b/docs/proposals/002-type-equality-constraints.md index 44562075e..33612720c 100644 --- a/docs/proposals/002-type-equality-constraints.md +++ b/docs/proposals/002-type-equality-constraints.md @@ -20,7 +20,7 @@ As of proposal [001](001-where-clauses.md), Slang allows for generic declaration Currently, the language only accepts *conformance* constraints of the form `T : IFoo`, where `T` is one of the parameters of the generic, and `IFoo` is either an `interface` or a conjunction of interfaces, which indicate that the type `T` must conform to `IFoo`. -This proposal is motivated by the observation that when an interface has associated types, there is currently no way for a programmer to introduce a generic that is only applicable when an associated type satisfies certain constriants. +This proposal is motivated by the observation that when an interface has associated types, there is currently no way for a programmer to introduce a generic that is only applicable when an associated type satisfies certain constraints. As an example, consider an interface for types that can be "packed" into a smaller representation for in-memory storage (instead of a default representation optimized for access from registers): @@ -146,7 +146,7 @@ The choice of how to represent equality constraints is more subtle. One option is to lower an equality constraint to *nothing* at the IR level, under the assumption that the casts that reference these constraints should lower to nothing. Doing so would introduce yet another case where the IR we generate doesn't "type-check." The other option is to lower a type equality constraint to an explicit generic parameter which is then applied via an explicit op to convert between the associated type and its known concrete equivalent. -The representation of the witnesses required to provide *arguments* for such parameters is something that hasn't been fully explored, so for now we prpose to take the first (easier) option. +The representation of the witnesses required to provide *arguments* for such parameters is something that hasn't been fully explored, so for now we propose to take the first (easier) option. ### Canonicalization @@ -156,7 +156,7 @@ Conformane constraints involving associated types should already be order-able a We propose the following approach: * Take all of the equality constraints that arise after any expansion steps -* Divide the types named on either side of any equality constraint into *equivalence classes*, where if `X == Y` is a constraint, then `X` and `Y` must in teh same equivalence class +* Divide the types named on either side of any equality constraint into *equivalence classes*, where if `X == Y` is a constraint, then `X` and `Y` must in the same equivalence class * Each type in an equivalence class will either be an associated type of the form `T.A.B...Z`, derived from a generic type parameter, or a *independent* type, which here means anything other than those associated types. * Because of the rules enforced during semantic checking, each equivalence class must have at least one associated type in it. * Each equivalence class may have zero or more independent types in it. diff --git a/docs/proposals/003-atomic-t.md b/docs/proposals/003-atomic-t.md index 76bb3bf81..c846ad3c7 100644 --- a/docs/proposals/003-atomic-t.md +++ b/docs/proposals/003-atomic-t.md @@ -143,7 +143,7 @@ For non-WGSL/Metal targets, we can simply lower the type out of existence into i `Atomic<T>` type exists in almost all CPU programming languages and is the proven way to express atomic operations over different architectures that have different memory models. WGSL and Metal follows this trend to require atomic operations being expressed -this way. This proposal is to make Slang follow this trend and make `Atomic<T>` the recommened way to express atomic operation +this way. This proposal is to make Slang follow this trend and make `Atomic<T>` the recommended way to express atomic operation going forward. # Future Work diff --git a/docs/proposals/004-initialization.md b/docs/proposals/004-initialization.md index 4605cd030..6a41cab23 100644 --- a/docs/proposals/004-initialization.md +++ b/docs/proposals/004-initialization.md @@ -8,7 +8,7 @@ Status Status: Design Approved, implementation in-progress. -Implemtation: N/A +Implementation: N/A Author: Yong He @@ -29,7 +29,7 @@ C++ has many different ways and syntax to initialize an object: through explicit A variable in C++ can also be in an uninitialized state after its declaration. HLSL inherits most of these behvior from C++ by allowing variables to be uninitialized. On the other hand, languages like C# and Swift has a set of well defined rules to ensure every variable is initialized after its declaration. -C++ allows using the initilization list syntax to initialize an object. The semantics of initialization lists depends on whether or not explicit constructors +C++ allows using the initialization list syntax to initialize an object. The semantics of initialization lists depends on whether or not explicit constructors are defined on the type. Proposed Approach @@ -92,10 +92,10 @@ void foo<T>() ### Synthesis of constructors for member initialization -If a type already defines any explicit constructors, do not synthesize any constructors for initializer list call. An intializer list expression +If a type already defines any explicit constructors, do not synthesize any constructors for initializer list call. An initializer list expression for the type must exactly match one of the explicitly defined constructors. -If the type doesn't provide any explicit constructors, the compiler need to synthesize the constructors for the calls that that the intializer +If the type doesn't provide any explicit constructors, the compiler need to synthesize the constructors for the calls that that the initializer lists translate into, so that an initializer list expression can be used to initialize a variable of the type. For each type, we will synthesize one constructor at the same visibility of the type itself: @@ -110,7 +110,7 @@ If `member0`'s type is not default initializable and the the member doesn't prov The synthesized constructor will be marked as `[Synthesized]` by the compiler, so the call site can inject additional compatibility logic when calling a synthesized constructor. -The body of the constructor will initialize each member with the value comming from the corresponding constructor argument if such argument exists, +The body of the constructor will initialize each member with the value coming from the corresponding constructor argument if such argument exists, otherwise the member will be initialized to its default value either defined by the init expr of the member, or the default value of the type if the type is default-initializable. If the member type is not default-initializable and a default value isn't provided on the member, then such the constructor synthesis will fail and the constructor will not be added to the type. Failure to synthesis a constructor is not an error, and an error will appear @@ -149,7 +149,7 @@ S obj = {}; S obj = S(); ``` -Note that initializer list of a single argument does not translate into a type cast, unlike the constructor call syntax. Initializing with a single element in the intializer list always translates directly into a constructor call. For example: +Note that initializer list of a single argument does not translate into a type cast, unlike the constructor call syntax. Initializing with a single element in the initializer list always translates directly into a constructor call. For example: ```csharp void test() { @@ -359,7 +359,7 @@ internal struct Visibility4 { // Visibility4 type is considered as C-style struct. // And we still synthesize a ctor for member initialization. - // Because Visiblity4 has no public members, the synthesized + // Because Visibility4 has no public members, the synthesized // ctor will take 0 arguments. internal int x = 1; internal int y = 2; @@ -398,7 +398,7 @@ so implicit initialization for these variables can come with serious performance ### Should `out` parameters be default initialized? -Following the same philosphy of not initializing any declarations, `out` parameters are also not default-initialized. +Following the same philosophy of not initializing any declarations, `out` parameters are also not default-initialized. Alternatives Considered ----------------------- diff --git a/docs/proposals/005-write-only-textures.md b/docs/proposals/005-write-only-textures.md index a68ed8c40..698ea6e86 100644 --- a/docs/proposals/005-write-only-textures.md +++ b/docs/proposals/005-write-only-textures.md @@ -9,7 +9,7 @@ Status Status: Design Review. -Implemtation: N/A +Implementation: N/A Author: Yong He diff --git a/docs/proposals/007-variadic-generics.md b/docs/proposals/007-variadic-generics.md index 8e5de1f04..8034c0312 100644 --- a/docs/proposals/007-variadic-generics.md +++ b/docs/proposals/007-variadic-generics.md @@ -212,7 +212,7 @@ void k<each T, let i : int>() {} // Error. void h<U = int, each T>() {} // OK. ``` -Addtionally, we establish these restrictions on how `expand` and `each` maybe used: +Additionally, we establish these restrictions on how `expand` and `each` maybe used: - The pattern type of an `expand` type expression must capture at least one generic type pack parameter in an `each` expression. - The type expression after `each` must refer to a generic type pack parameter, and the `each` expression can only appear inside an `expand` expression. @@ -222,7 +222,7 @@ Similarly, when using `expand` and `each` on values, we require that: - The pattern expression of an `expand` expression must capture at least one value whose type is a generic type pack parameter. - The expression after `each` must refer to a value whose type is a generic type pack parameter, and the `each` expression can only appear inside an `expand` expression. -Combined with type euqality constriants, variadic generic type pack can be used to define homogeneously typed parameter pack: +Combined with type euqality constraints, variadic generic type pack can be used to define homogeneously typed parameter pack: ``` void calcInts<each T>(expand each T values) where T == int { @@ -653,8 +653,8 @@ by packing up all the values computed at each "loop iteration" in an `IRMakeValu } ``` -With this, we can replace the original `IRExpand` inst with `%expand` and specailization is done. The specialized instructions like `IRGetTupleElement(%v, 0)` will be picked up -in the follow-up step during specalization and replaced with the actual value at the specified index since `%v` is a known value pack represented by `IRMakeValuePack`. So after +With this, we can replace the original `IRExpand` inst with `%expand` and specialization is done. The specialized instructions like `IRGetTupleElement(%v, 0)` will be picked up +in the follow-up step during specialization and replaced with the actual value at the specified index since `%v` is a known value pack represented by `IRMakeValuePack`. So after folding and other simplifications, we should result in ``` %expand = IRMakeValuePack(2,3,4) @@ -673,7 +673,7 @@ After the specialization pass, there should be no more `IRExpand` and `IRExpandT Alternatives Considered ----------------------- -We considered the C++ `...` oeprator syntax and Swift's `repeat each` syntax and ended up picking Swift's design because it is easier to parse and is less ambiguous. Swift is strict about requiring `each` to precede a generic type pack parameter so `void f<each T>(T v)` is not a valid syntax to prevent confusion on what `T` is in this context. In Slang we don't require this because `expand each T` is always simplified down to `T`, and refer to the type pack. +We considered the C++ `...` operator syntax and Swift's `repeat each` syntax and ended up picking Swift's design because it is easier to parse and is less ambiguous. Swift is strict about requiring `each` to precede a generic type pack parameter so `void f<each T>(T v)` is not a valid syntax to prevent confusion on what `T` is in this context. In Slang we don't require this because `expand each T` is always simplified down to `T`, and refer to the type pack. We also considered not adding variadic generics support to the language at all, and just implement `Tuple` and `IFunc` as special system builtin types, like how it is done in C#. However we believe that this approach is too limited when it comes to what the user can do with tuples and `IFunc`. Given Slang's position as a high performance GPU-first language, it is more important for Slang than other CPU languages to have a powerful type system that can provide zero-cost abstraction for meta-programming tasks. That lead us to believe that the language and the users can benefit from proper support of variadic generics. diff --git a/docs/proposals/012-language-version-directive.md b/docs/proposals/012-language-version-directive.md index 4c8178ffb..3a75e5069 100644 --- a/docs/proposals/012-language-version-directive.md +++ b/docs/proposals/012-language-version-directive.md @@ -53,7 +53,7 @@ There are some key lessons from the history of GLSL that are worth paying attent * When OpenGL ES was introduced, the OpenGL ES Shading Language also used an identical `#version` directive, but the meaning of a given version number was different between GLSL and GLSL ES (that is, different language features/capabilities were implied by the same `#version`, depending on whether one was compiling with a GLSL or GLSL ES compiler). The use of the optional profile name is highly encouraged when there might be differences in capability not encoded by just the version number. -* Initially, the version numbers for OpenGL and GLSL were not aligned. For example, OpenGL 2.0 used GLSL 1.10 by default. This led to confusion for developers, who needed to keep track of what API version coresponded to what language version. The version numbers for OpenGL and GLSL became aligned starting with OpenGL 3.3 and GLSL 3.30. +* Initially, the version numbers for OpenGL and GLSL were not aligned. For example, OpenGL 2.0 used GLSL 1.10 by default. This led to confusion for developers, who needed to keep track of what API version corresponded to what language version. The version numbers for OpenGL and GLSL became aligned starting with OpenGL 3.3 and GLSL 3.30. * A common, but minor, gotcha for developers is that the GLSL `#version` directive can only be preceded by trivia (whitespace and comments) and, importantly, cannot be preceded by any other preprocessor directives. This limitation has created problems when applications want to, e.g., prepend a sequence of `#define`s to an existing shader that starts with a `#version`. @@ -106,10 +106,10 @@ We will differentiate between two kinds of versions, which will have aligned num This proposal doesn't intend to dictate the format used for version numbers, since that is tied into the release process for the Slang toolset. We expect that version numbers will start with a year, so that, e.g., `2025.0` would be the first release in the year 2025. -A given version of the Slang toolset (e.g, `2024.10`) should always support the matching language verson. +A given version of the Slang toolset (e.g, `2024.10`) should always support the matching language version. If this proposal is accepted, we expect releases of the Slang toolset to support a *range* of language versions, ideally covering a full year or more of backwards compatibility. -This proposal does not seek to make any guarantees about the level of backwards compatiblity, leaving that the Slang project team to determine in collaboration with users. +This proposal does not seek to make any guarantees about the level of backwards compatibility, leaving that the Slang project team to determine in collaboration with users. ### `#language` Directives ### @@ -151,7 +151,7 @@ Alternatives Considered ### Compiler Options ### The main alternative here is to allow the language version to be specified via compiler options. -The exising `-lang` option for `slangc` could be extended to include a language version: e.g., `slang2025.1`. +The existing `-lang` option for `slangc` could be extended to include a language version: e.g., `slang2025.1`. This proposal is motivated by extensive experience with the pain points that arise when semantically-significant options, flags, and capabilities required by a project are encoded not in its source code, but only in its build scripts or other configuration files. Anybody who has been handed a single `.hlsl` file and asked to simply compile it (e.g., to reproduce a bug or performance issue) likely knows the litany of questions that need to be answered before that file is usable: what is the entry point name? What stage? What shader model? diff --git a/docs/proposals/implementation/ast-ir-serialization.md b/docs/proposals/implementation/ast-ir-serialization.md index 248b752a1..cc65c0715 100644 --- a/docs/proposals/implementation/ast-ir-serialization.md +++ b/docs/proposals/implementation/ast-ir-serialization.md @@ -19,7 +19,7 @@ Either the entire `Decl` hierarchy of the AST is deserialized and turned into in Similarly, we can either construct the `IRInst` hierarchy for an entire module, or none of it. Releases of the Slang compiler typically included a serialized form of the core module, and the runtime cost of deserializing this module has proven to be a problem for users of the compiler. -Becuse parts of the Slang compiler are not fully thread-safe/reentrant, the core module must be deserialized for each "global session," so that deserialization cost is incurred per-thread in scenarios with thread pools. +Because parts of the Slang compiler are not fully thread-safe/reentrant, the core module must be deserialized for each "global session," so that deserialization cost is incurred per-thread in scenarios with thread pools. Even in single-threaded scenarios, the deserialization step adds significantly to the startup time for the compiler, making single-file compiles less efficient than compiling large batches of files in a single process. Overview of Proposed Solution @@ -49,7 +49,7 @@ The linker creates a *fresh* `IRModule` for the linked result, and clones/copies 1. Given an instruction in an input module to be copied over, use an `IRBuilder` on the output module to create a deep copy of that instruction and its children. -2. Whenever an instruction being copied over references another top-level instruction local to the same input module (that is, one without a linkage decoration), either construct a deep copy of the refereneced instruction in the output module, or find and re-use a copy that was made previously. +2. Whenever an instruction being copied over references another top-level instruction local to the same input module (that is, one without a linkage decoration), either construct a deep copy of the referenced instruction in the output module, or find and re-use a copy that was made previously. 3. Whenever an instruction being copied over references a top-level instruction that might be resolved to come from another module (that is, one with a linkage decoration), use the mangled name on the linkage decoration to search *all* of the input modules for candidate instructions that match. Use some fancy logic to pick one of them (the details aren't relevant at this exact moment) and then copy the chosen instruction over, more or less starting at (1) above. @@ -117,7 +117,7 @@ The two main ways that the child declarations are accessed are: Currently the `memberDictionary` field is private, and has access go through methods that check whether the dictionary needs to be rebuilt. The `members` field should also be made private, so that we can carefully intercept any code that wants to enumerate all members of a declaration. -We should probably also make the `memberDictionary` field map from a name to the *index* of a declaration in `members`, instead of direclty to a `Decl*`. +We should probably also make the `memberDictionary` field map from a name to the *index* of a declaration in `members`, instead of directly to a `Decl*`. > Note: We're ignoring the `ContainerDecl::transparentMembers` field here, but it does need to be taken into account in the actual implementation. @@ -137,7 +137,7 @@ The `members` array will either be empty, or will be correctly-sized for the num The entries in `members` may be null, however, if the corresponding child declaration has not been deserialized. We will need to attach a pointer to information related to lazy-loading to the `ContainerDecl`. -The simplest approach would be to add a field to `ContainerDecl`, but we could also consider using a custom `Modifier` if we are concerend about bloat. +The simplest approach would be to add a field to `ContainerDecl`, but we could also consider using a custom `Modifier` if we are concerned about bloat. #### Enumeration @@ -187,7 +187,7 @@ These complications lead to two big consequences for the encoding: * The array of *AST entries* will not just contain the entries for top-level `Decl`s. It needs to contain an entry for each `Decl` that might be referenced from elsewhere in the AST. For simplicity, it will probably contain *all* `Decl`s that are not explicitly stripped as part of producing the serialized AST. -* The array won't even consist of just `Decl`s. It will also need to have entries for things like `DeclRef`s and `Type`s that can also be referened as operands of AST nodes. +* The array won't even consist of just `Decl`s. It will also need to have entries for things like `DeclRef`s and `Type`s that can also be referenced as operands of AST nodes. As a stab at a simple representation, each AST entry should include: @@ -195,7 +195,7 @@ As a stab at a simple representation, each AST entry should include: * A range of bytes in the raw data that holds the serialized representation of that node (e.g., its operands) -An entry for a `ContainerDecl` should include (whether direclty or encoded in the raw data...) +An entry for a `ContainerDecl` should include (whether directly or encoded in the raw data...) * A contiguous range of AST entry indices that represent the direct children of the node, in declaration order (the order they'd appear in `ContainerDecl::members`) @@ -208,7 +208,7 @@ Given the above representation, there is no need to explicitly encode the parent Given an AST entry index for a `Decl`, we can find its parent by recursing through the hierarchy starting at the root, and doing a binary search at each hierarchy level to find the (unique) child declaration at that level which contains that index in its range of descendents. When there is a request to on-demand deserialize a `Decl` based on its AST entry index, we would need to first deserialize each of its ancestors, up the hierarchy. -That on-demand deserialization of the ancestors can follow the flow given above for recursively walking the hirarchy to find which declaration at each level contains the given index. +That on-demand deserialization of the ancestors can follow the flow given above for recursively walking the hierarchy to find which declaration at each level contains the given index. In order to support lookup of members of a declaration by name, we propose the following: diff --git a/docs/proposals/legacy/001-basic-interfaces.md b/docs/proposals/legacy/001-basic-interfaces.md index 6083c8ae9..669537b42 100644 --- a/docs/proposals/legacy/001-basic-interfaces.md +++ b/docs/proposals/legacy/001-basic-interfaces.md @@ -232,7 +232,7 @@ These would be the first `interface`s officially exposed by the core module. While most of our existing code written in Slang uses an `I` prefix as the naming convention for `interface`s (e.g., `IThing`), we have never really discussed that choice in detail. Whatever we decide to expose for this stuff is likely to become the de facto convention for Slang code. -The `I` prefix is precedented in COM and C#/.net/CLR, which are likely to be familiar to many devleopers using Slang. +The `I` prefix is precedented in COM and C#/.net/CLR, which are likely to be familiar to many developers using Slang. Because of COM, it is also the convention used in the C++ API headers for Slang and GFX. The Rust/Swift languages do not distinguish between traits/protocols and other types. diff --git a/docs/proposals/legacy/002-api-headers.md b/docs/proposals/legacy/002-api-headers.md index 66b649228..5efbc1d16 100644 --- a/docs/proposals/legacy/002-api-headers.md +++ b/docs/proposals/legacy/002-api-headers.md @@ -29,7 +29,7 @@ We know that we cannot remove support for difficult cases, but it would be good Related Work ------------ -There are obviously far too many C/C++ APIs and approachs to design for C/C++ APIs for us to review them all. +There are obviously far too many C/C++ APIs and approaches to design for C/C++ APIs for us to review them all. We will simply note a few key examples that can be relevant for comparison. The gold standard for C/C++ APIs is ultimately plain C. Plain C is easy for most systems programmers to understand and benefits from having a well-defined ABI on almost every interesting platform. FFI systems for other languages tend to work with plain C APIs. Clarity around ABI makes it easy to know what changes/additions to a plain C API will and will not break binary compatibility. The Cg compiler API and the Vulkan GPU API are good examples of C-based APIs in the same domains as Slang and GFX, respectively. These APIs reveal some of the challenges of using plain C for large and complicated APIs: @@ -64,7 +64,7 @@ Across such APIs, we see a wide variety of strategies to dealing with extensibil * Vulkan uses "desc" structures (usually called "info" or "create info" structures), which contain a baseline set of state/fields, along with a linked list of dynamically-typed/-tagged extension structures. New functionality that only requires changes to "desc" structures can be added by defining a new tag and extension structure. New operations are added in a manner similar to OpenGL. -* D3D12 also uses COM interfaces and "desc" structures (although now officialy called "descriptions" to not overload the use of "descriptor" in descriptor tables), much like D3D11, and sometimes uses the same approach to extensibility (e.g., there are currently `ID3D12Device`, `ID3D12Device`, ... `ID2D12Device9`). In addition, D3D12 has also added two variations on Vulkan-like models for creating pipeline state (`ID3D12Device2::CreatePipelineState` and `ID3D12Device5::CreateStateObject`), using a notion of more fine-grained "subojects" that are dynamically-typed/-tagged and each have their own "desc". +* D3D12 also uses COM interfaces and "desc" structures (although now officially called "descriptions" to not overload the use of "descriptor" in descriptor tables), much like D3D11, and sometimes uses the same approach to extensibility (e.g., there are currently `ID3D12Device`, `ID3D12Device`, ... `ID2D12Device9`). In addition, D3D12 has also added two variations on Vulkan-like models for creating pipeline state (`ID3D12Device2::CreatePipelineState` and `ID3D12Device5::CreateStateObject`), using a notion of more fine-grained "subojects" that are dynamically-typed/-tagged and each have their own "desc". It is important to note that even with the nominal flexibility that COM provides around versioning, D3D12 has opted for a more fine-grained approach when dealing with something as complicated as GPU pipeline state. @@ -91,7 +91,7 @@ At the end of this document there is a lengthy code block that sketches a possib Questions --------- -### Will we generate all or some of the API header? If so, what will be the "ground truth" verison? +### Will we generate all or some of the API header? If so, what will be the "ground truth" version? Note that Vulkan and SPIR-V benefit from having ground-truth computer-readable definitions, allowing both header files and tooling code to be generated. @@ -278,7 +278,7 @@ namespace slang members initializers in `slang::SessionDesc`, but it *will* compile under C++14 and later. - If we want to deal with C++11 compatiblity in that case, we can, but + If we want to deal with C++11 compatibility in that case, we can, but it would slightly clutter up the way we declare these things. Realistically, we'd just split the two types: @@ -325,7 +325,7 @@ namespace slang struct SessionDesc0 { ... the original ... }; struct SessionDesc1 { ... the new one ... }; - typedef SessionDesc SesssionDesc1; + typedef SessionDesc SessionDesc1; At the point where we introduce a second version, it is probably the right time to enable developers to lock in to any version they choose. In the code above @@ -334,7 +334,7 @@ namespace slang at the point they compile. (If we wanted to get really "future-proof" we'd define every struct with the `0` - prefix right out of the gate, and always have the `typedef` in place. I'm not conviced + prefix right out of the gate, and always have the `typedef` in place. I'm not convinced that would ever pay off.) I expect most of this to be a non-issue if we are zealous about using fine-grained @@ -368,7 +368,7 @@ namespace slang UUID const& uuid, void** outObject) = 0; - /* Instead, most users will direclty call the operations only through + /* Instead, most users will directly call the operations only through wrappers that provide conveniently type-safe behavior: */ inline Result createCompileRequest( @@ -574,7 +574,7 @@ extern "C" and also to provide versioning support. We could define the same set of overloads here, with the same names, for - use by clients who don't actually care about C compatiblity but just like + use by clients who don't actually care about C compatibility but just like a C-style API. That is probably worth doing. Otherwise, we realistically need to start defining some de-facto naming @@ -612,7 +612,7 @@ extern "C" /* Finally, the C API level is where we should define the core factory entry point for creating and initializing the Slang global session (just like - in the current header). Here we jsut generalize it for creaitng "any" global + in the current header). Here we just generalize it for creaitng "any" global object, based on a UUID and a bunch of descs. */ SLANG_API SlangResult slang_createObject( @@ -645,7 +645,7 @@ generating as much of the API as possible anyway. /* Basic Types */ - /* We just define the basic types direclty, without the indirection + /* We just define the basic types directly, without the indirection through the declarations in the `slang::` namespace. */ @@ -846,7 +846,7 @@ use of exceptions instead of `Result`s: SLANG_SMART_PTR(ICompileRequest) createCompileRequest( CompileRequestDesc const& desc) { - SLANG_SMART_PTR(ICompileReqest) compileRequest; + SLANG_SMART_PTR(ICompileRequest) compileRequest; SLANG_THROW_IF_FAIL(_createCompileRequest( &desc, 1, SLANG_UUID_OF(IComileRequest), comileRequest.writeRef())); return compileRequest; @@ -857,7 +857,7 @@ use of exceptions instead of `Result`s: #endif } -Both for the sake of C API and especialy for gfx (both C and C++), we should consider +Both for the sake of C API and especially for gfx (both C and C++), we should consider defining some coarse-grained aggregate desc types as utilities: struct SimpleRasterizationPipelineStateDesc @@ -886,7 +886,7 @@ defining some coarse-grained aggregate desc types as utilities: // List<T> members for attachments, etc. rather than just pointer-and-count: private: List<AttachmentDesc> colorAttachments; - public: AttachmentDesc& addColorAttachement(); + public: AttachmentDesc& addColorAttachment(); // There should also be convenience constructors common cases // (especially relevant for things like textures). @@ -924,7 +924,7 @@ defining some coarse-grained aggregate desc types as utilities: }; While the implementation of this monolithic desc types would not necessarily be pretty, -it would enable users who want the benefits of the "one big struct" appraoch to get +it would enable users who want the benefits of the "one big struct" approach to get what they seem to want. The next step down this road is to take these aggregate desc types and turn them into diff --git a/docs/proposals/legacy/003-error-handling.md b/docs/proposals/legacy/003-error-handling.md index 23c7bdc40..e8fb44402 100644 --- a/docs/proposals/legacy/003-error-handling.md +++ b/docs/proposals/legacy/003-error-handling.md @@ -43,10 +43,10 @@ Related Work In the absence of language support, developers typically signal and propagate errors using *error codes*. The COM `HRESULT` type is a notable example of a well-defined system for using error codes in C/C++ and other languages. Error codes have the benefit of being easy to implement, and relatively light-weight. The main drawback of error codes is that developers often forget to check and/or propagate them, and when they do remember to do so it adds a lot of boilerplate. -Additonally, reserving the return value of every function for returning an error code makes code more complex because the *actual* return value must be passed via a function parameter. +Additionally, reserving the return value of every function for returning an error code makes code more complex because the *actual* return value must be passed via a function parameter. C++ uses *exceptions* for errors in various categories, including unpredictable but recoverable failures. -Propagation of errors up the call stack is entirely automatic, with unwinding of call frames and destruction of their local state occuring as part of the search for a handler. +Propagation of errors up the call stack is entirely automatic, with unwinding of call frames and destruction of their local state occurring as part of the search for a handler. Neither functions that may throw nor call sites to such functions are syntactically marked. Exceptions in C++ have often been implemented in ways that add overhead and require complicated support in platform ABIs and intermediate languages to support. @@ -66,7 +66,7 @@ Functions that return `X` and those that return `Result<X, ...>` are not directl Swift provides more syntactic support for errors than Rust, although the underlying mechanism is similar. A Swift function may have `throws` added between the parameter list and return type to indicate that a function may yield an error. All errors in Swift must implement the `Error` protocol, and all functions that can `throw` may produce any `Error` (although there are proposals to extend Swift with "typed `throws`"). -Any call site to a `throws` function must have a prefix `try` (e.g., `try f(a, b)`), which works simiarly to Rust's `?`; any error produced by the called function is propagated, and the ordinary result is returned. +Any call site to a `throws` function must have a prefix `try` (e.g., `try f(a, b)`), which works similarly to Rust's `?`; any error produced by the called function is propagated, and the ordinary result is returned. Swift provides an explicit `do { ... } catch ...` construct that allows handlers to be established. It also provides for conversion between exceptions and an explicit `Result<T,E>` type, akin to Rust's. Higher-order functions may be declared as `rethrows` to indicate that whether or not they throw depends on whether or not any of their function-type parameters is actually a `throws` function at a call site. @@ -203,7 +203,7 @@ let y : int = 1 + _tmp; ### Desugar `throw` Expressions -For every `throw` site in a function body, there will either be no in-scope `catch` clause that matches the type thrown, or there will be eactly one most-deeply-nested `catch` that statically matches. +For every `throw` site in a function body, there will either be no in-scope `catch` clause that matches the type thrown, or there will be exactly one most-deeply-nested `catch` that statically matches. Front-end semantic checking should be able to associate each `throw` with the appropriate `catch` if any. For `throw` sites with no matching `catch`, the operation simply translates to a `return` of the thrown error (because of the way we transformed the function signature). diff --git a/docs/proposals/legacy/005-components.md b/docs/proposals/legacy/005-components.md index b257140a7..ff53d0f77 100644 --- a/docs/proposals/legacy/005-components.md +++ b/docs/proposals/legacy/005-components.md @@ -239,7 +239,7 @@ class X ``` In the above, an instance of `X` can always find the `Y` it depends on easily and (relatively) efficiently. -There is no particularly high overhead to having `X` diretly store an indirect reference to `Y` (at least not for coarse-grained units), and it is trivial for multiple units like `X` to all share the same *instance* of `Y` (potentially even including mutable state, for applications that like that sort of thing). +There is no particularly high overhead to having `X` directly store an indirect reference to `Y` (at least not for coarse-grained units), and it is trivial for multiple units like `X` to all share the same *instance* of `Y` (potentially even including mutable state, for applications that like that sort of thing). In general most CPU languages (and especially OOP ones) can express the concepts of "is-a" and "has-a" but they often don't distinguish between when "has-as" means "refers-to-and-depends-on-a" vs. when it means "aggregates-and-owns-a". This is important when looking at a GPU language like Slang, where "aggergates-and-owns-a" is easy (we have `struct` types), but "refers-to-and-depends-on-a" is harder (not all of our targets can really support pointers). @@ -279,7 +279,7 @@ interface IMaterialSystem void doMaterialStuff(); } -__component_type DefualtMaterialSystem : IMaterialSystem +__component_type DefaultMaterialSystem : IMaterialSystem { __require lighting : ILightingSystem; @@ -365,7 +365,7 @@ At the point where an `__aggregate SomeType` member is declared, the front-end s For example, because `DefaultIntegratorSystem` declares `__require IMaterialSystem`, the compiler searches in the current context for a value that can provide that interface. It finds a single suitable value: the value implicitly defined by `__aggregate DefaultMaterialSystem`, and thus "wires up" the input dependency of the `DefaultIntegratorSystem`. -It is posible for a `__require` in an `__aggregate`d member to be satisfied via another `__require` of its outer type: +It is possible for a `__require` in an `__aggregate`d member to be satisfied via another `__require` of its outer type: ``` __component_type MyUnit @@ -384,7 +384,7 @@ While the above examples do not show it, component types should be allowed to co Detailed Explanation -------------------- -Component types need to be restricted in where and how they can be used, to avoid creating situations that would give them all the flexiblity of arbitrary `class`es. +Component types need to be restricted in where and how they can be used, to avoid creating situations that would give them all the flexibility of arbitrary `class`es. The only places where a component type may be used are: * `__require` and `__aggregate` declarations @@ -434,7 +434,7 @@ Note that when the generated code invokes an operation through one of the `__agg Effectively, the compiler generates all of the boilerplate parameter-passing that the programmer would have otherwise had to write by hand. -It might or might not be obvious that the notion of "component type" being described here has a clear correspondance to the `IComponentType` interface provided by the Slang runtime/compilation API. +It might or might not be obvious that the notion of "component type" being described here has a clear correspondence to the `IComponentType` interface provided by the Slang runtime/compilation API. It should be possible for us to provide reflection services that allow a programmer to look up a component type by name and get an `IComponentType`. The existing APIs for composing, specializing, and linking `IComponentType`s should Just Work for explicit `__component_type`s. Large aggregates akin to `MyProgram` above can be defined entirely via the C++ `IComponentType` API at program runtime. @@ -456,7 +456,7 @@ That last point is important, since a component type allows users to define a co ### Can the `__component_type` construct just be subsumed by either `struct` or `class`? -Maybe. The key challenge is that component types need to provide the "look and feel" of by-refernece re-use rather than by-value copying. A `__require T` should effectively act like a `T*` and not a bare `T` value, so I am reluctant to say that should map to `struct`. +Maybe. The key challenge is that component types need to provide the "look and feel" of by-reference re-use rather than by-value copying. A `__require T` should effectively act like a `T*` and not a bare `T` value, so I am reluctant to say that should map to `struct`. ### But what about `[mutating]` operations and writing to fields of component types, then? @@ -484,7 +484,7 @@ __component_type C { __require A; ... } __component_type D { __require B; __require C; ... } ``` -The Spark shading language research project used multiple mixin class inheritance to compose units of shader code akin to what are being proposed here as coponent types (hmm... I guess that should go into related work...). +The Spark shading language research project used multiple mixin class inheritance to compose units of shader code akin to what are being proposed here as component types (hmm... I guess that should go into related work...). In general, using inheritance to model something that isn't an "is-a" relationship is poor modeling. Inheritance as a modelling tool cannot capture some patterns that are possible with `__aggregate` (notably, with mixin inheritance you can't get multiple "copies" of a component). diff --git a/docs/proposals/legacy/006-artifact-container-format.md b/docs/proposals/legacy/006-artifact-container-format.md index 81910151b..04daf3bd4 100644 --- a/docs/proposals/legacy/006-artifact-container-format.md +++ b/docs/proposals/legacy/006-artifact-container-format.md @@ -491,7 +491,7 @@ The grouping - how does it actually work? It might require specifying what group An advantage to this approach is that policy of how naming works as a user space problem. It is also powerful in that it allows control on compilation that has some independence from the name. -We could have some options that are named, but do not appear as part of the name/path within the container. The purpose of this is to allow customization of a compilation, without that customization necessarily appearing withing the application code. The container could store group of named options that is used, such that it is possible to recreate the compilation or perhaps to detect there is a difference. +We could have some options that are named, but do not appear as part of the name/path within the container. The purpose of this is to allow customization of a compilation, without that customization necessarily appearing within the application code. The container could store group of named options that is used, such that it is possible to recreate the compilation or perhaps to detect there is a difference. ### JSON options @@ -776,7 +776,7 @@ Discussion: Container A typical container will contain kernels - in effect blobs. The blobs themselves, or the blob names are not going to be sufficient to express the amount of information that is necessary to meet the goals laid out at the start of this document. Some extra information may be user supplied. Some extra information might be user based to know how to classify different kernels. Therefore it is necessary to have some system to handle this metadata. -As previously discussed the underlying container format is a file system. Some limited information could be infered from the filename. For example a .spv extension file is probably SPIR-V blob. For more rich meta data describing a kernel something more is needed. Two possible approaches could be to have a 'manifest' that described the contents of the container. Another approach would to have a file associated with the kernel that describes it's contents. +As previously discussed the underlying container format is a file system. Some limited information could be inferred from the filename. For example a .spv extension file is probably SPIR-V blob. For more rich meta data describing a kernel something more is needed. Two possible approaches could be to have a 'manifest' that described the contents of the container. Another approach would to have a file associated with the kernel that describes it's contents. Single Manifest Pros |
