diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index ad17c6ccf..3c662a6e2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -52,6 +52,8 @@ jobs: runs-on: ${{ matrix.host.os }} steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: .build @@ -59,10 +61,6 @@ jobs: restore-keys: | ${{ runner.os }}-spm- - - uses: actions/checkout@v3 - - - run: git config --global core.autocrlf input - - name: Build and Test uses: devcontainers/ci@v0.3 with: @@ -108,8 +106,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - run: git config --global core.autocrlf input - - name: Setup swift uses: swift-actions/setup-swift@v1 with: @@ -148,7 +144,7 @@ jobs: https://github.com/apple/swift-package-manager/issues/6595" && false) ) build-native-windows: - name: "Native: windows-latest/release" + name: "Native, no testing: windows-latest/release" strategy: fail-fast: false runs-on: windows-latest @@ -159,8 +155,6 @@ jobs: branch: swift-5.8.1-release tag: 5.8.1-RELEASE - - run: | - git config --global core.autocrlf input - uses: actions/checkout@v3 - name: Swift version diff --git a/.github/workflows/doc-extraction.yml b/.github/workflows/doc-extraction.yml index 0176d8c0c..8ca1a71a5 100644 --- a/.github/workflows/doc-extraction.yml +++ b/.github/workflows/doc-extraction.yml @@ -27,6 +27,8 @@ jobs: build: runs-on: macos-latest steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: .build @@ -34,9 +36,6 @@ jobs: restore-keys: | ${{ runner.os }}-spm- - - uses: actions/checkout@v3 - - run: git config --global core.autocrlf input - - name: Setup swift uses: swift-actions/setup-swift@v1 with: @@ -85,7 +84,7 @@ jobs: export PKG_CONFIG_PATH for TARGET in ${EXTRACTION_TARGETS}; do mkdir -p _site/docc/"$TARGET" - swift package --allow-writing-to-directory ./_site \ + Tools/retry-once swift package --allow-writing-to-directory ./_site \ generate-documentation \ --target "$TARGET" \ --output-path _site/docc/"${TARGET}" \ @@ -128,7 +127,7 @@ jobs: - name: Fix permissions run: | - chmod -v -R +rX "_site/" | while read line; do + chmod -v -R +rX "_site/" | while read -r line; do echo "::warning title=Invalid file permissions automatically fixed::$line" done diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 4ef02c9d4..ac66e7bf7 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -1,5 +1,9 @@ name: Format check +defaults: + run: + shell: bash -eo pipefail {0} + on: push: branches: [ main ] @@ -21,27 +25,15 @@ on: jobs: build: name: Format check - runs-on: ubuntu-latest + runs-on: macos-latest + steps: - - uses: actions/cache@v3 - with: - path: .build - key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }} - restore-keys: | - ${{ runner.os }}-spm- + - name: install swift-format + run: | + brew install swift-format - name: Checkout uses: actions/checkout@v3 - - run: git config --global core.autocrlf input - - - name: Setup swift - uses: swift-actions/setup-swift@v1 - with: - swift-version: "5.8" - - - name: Swift version - run: swift --version - - name: Check code format - run: Tools/run-swift-format.sh -d lint + run: swift-format lint -r --configuration .swift-format.json -p Sources Tests Package.swift diff --git a/.github/workflows/spell-check.yml b/.github/workflows/spell-check.yml new file mode 100644 index 000000000..0cd6cae89 --- /dev/null +++ b/.github/workflows/spell-check.yml @@ -0,0 +1,13 @@ +name: Spell check +on: [pull_request, push] + +jobs: + run: + name: Spell Check using typos + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v3 + + - name: Check spelling of file.txt + uses: crate-ci/typos@v1.0.4 diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 000000000..77ae59d8d --- /dev/null +++ b/.typos.toml @@ -0,0 +1,3 @@ +[default.extend-words] +inout = "inout" # Mutable projection keyword +olt = "olt" # Abbreviation for "ordered less than" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7eb9db95d..8e016ff51 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,8 +53,17 @@ You will need to manually [synchronize it](https://docs.github.com/en/pull-reque Make sure your changes do not break anything by running all existing tests. We also ask that you write tests for the code you want to contribute. +Ensure your changes do not intruduce any spelling errors. We use [typos-action] +to ensure no mistakes creep in. To run locally, [install typos] and run `typos` +to run a check and `typos -w` to automatically apply suggestions. If you run +into any false positives see the [typos false positives documentation]. + Before issuing a pull request we ask that you squash all your commits into a single logical commit that describes your contribution. We may ask you to commit additional changes while your pull request is under review. Once everything looks good, squash those additional commits before merging. Do not hesitate to reach out if you are lost in the process. + +[typos-action]: https://github.com/marketplace/actions/typos-action +[install typos]: https://github.com/crate-ci/typos#install +[typos false positives documentation]: https://github.com/crate-ci/typos#false-positives diff --git a/Docs/DefiniteInitialization.md b/Docs/DefiniteInitialization.md index d2a2ebb1b..f7c769877 100644 --- a/Docs/DefiniteInitialization.md +++ b/Docs/DefiniteInitialization.md @@ -1,6 +1,6 @@ # Definite (De)Initialization -Definite (De)Initialization (DI, a.k.a. [definite assignment analyis](https://en.wikipedia.org/wiki/Definite_assignment_analysis) is a mandatory transformation pass that is applied on Hylo's intermediate representation, before code generation. +Definite (De)Initialization (DI, a.k.a. [definite assignment analysis](https://en.wikipedia.org/wiki/Definite_assignment_analysis) is a mandatory transformation pass that is applied on Hylo's intermediate representation, before code generation. This pass ensures that all objects are initialized before use (definite initialization) and deinitialized before the end of their storage's lifetime (definite deinitialization), inserting additional instructions if necessary. The pass is expected to run early in the IR transformation pipeline, after __Implicit Return Insertion__ and __Unreachable Code Elimination__. diff --git a/Docs/RemoteParts.md b/Docs/RemoteParts.md index 1b9aa2090..f3009b927 100644 --- a/Docs/RemoteParts.md +++ b/Docs/RemoteParts.md @@ -24,7 +24,7 @@ The compiler should emit a warning if a remote part is taken as a parameter of a Local `var` bindings operate differently than stored `var` properties. When we assign into a local `var`, the value that it held is destroyed (unless we do the `copy(into:)` optimization). When we assign into a stored `var`, the value of its container is modified in place. -The thoeretical rationale is that a dotted access is always projection: the LHS of `foo.bar = 2` is not actually a `var`. +The theoretical rationale is that a dotted access is always projection: the LHS of `foo.bar = 2` is not actually a `var`. Example: ``` diff --git a/Docs/Subscripts.md b/Docs/Subscripts.md index 36490cb0f..eefecf830 100644 --- a/Docs/Subscripts.md +++ b/Docs/Subscripts.md @@ -133,7 +133,7 @@ These two restrictions serve two goals: 1. Guarantee that continuations cannot break control flow and cannot. 2. Handle overlapping projections that do not nest. -One over-approximation of a continuation's scope is the dominance frontier of the basic block in which the projection starts, excluding the instructions before the start of the projeciton. +One over-approximation of a continuation's scope is the dominance frontier of the basic block in which the projection starts, excluding the instructions before the start of the projection. In the above example, the continuation associated with `t` would contain all instructions in `loop.body` after the definition of `%t`. Using dominance frontiers also obviates the difficulty to identify definitions may escape a continuation. diff --git a/Library/Hylo/Core/Bitcast.hylo b/Library/Hylo/Core/Bitcast.hylo index 26e05dafc..8f6589a0c 100644 --- a/Library/Hylo/Core/Bitcast.hylo +++ b/Library/Hylo/Core/Bitcast.hylo @@ -1,11 +1,11 @@ /// Projects `value` with its memory representation reinterpreted as a value of type `U`. public subscript unsafe_bitcast(_ value: T): U { let { - sink let p = Pointer.to[value].copy().value - yield p as* (remote let U) + sink let p: Pointer = Pointer(type_punning: pointer[to: value]) + yield p.unsafe[] } inout { - sink let p = Pointer.to[value].copy().value - yield &(p as* (remote inout U)) + sink let p: PointerToMutable = PointerToMutable(type_punning: pointerToMutable[&value]) + yield &p.unsafe[] } } diff --git a/Library/Hylo/Core/CVoidPointer.hylo b/Library/Hylo/Core/CVoidPointer.hylo new file mode 100644 index 000000000..120790aec --- /dev/null +++ b/Library/Hylo/Core/CVoidPointer.hylo @@ -0,0 +1,42 @@ +// This type only exists because we don't seem to be able to make generic types like Pointer ForeignConvertible. +/// A pointer type for use in @ffi signatures. +type CVoidPointer: Regular { + + /// The underlying representation. + var base: Builtin.ptr + + memberwise init + +} + +public conformance CVoidPointer: ForeignConvertible { + + public typealias ForeignRepresentation = Builtin.ptr + + public init(foreign_value: sink Builtin.ptr) { + &self.base = foreign_value + } + + public fun foreign_value() -> Builtin.ptr { + base + } + +} + +public conformance CVoidPointer: Copyable { + + /// Returns an equivalent instance. + public fun copy() -> Self { + CVoidPointer(base: base) + } + +} + +public conformance CVoidPointer: Equatable { + + /// Returns `true` iff `other` has an equivalent value. + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_ptr(base, other.base)) + } + +} diff --git a/Library/Hylo/Core/Comparable.hylo b/Library/Hylo/Core/Comparable.hylo new file mode 100644 index 000000000..05cc1c066 --- /dev/null +++ b/Library/Hylo/Core/Comparable.hylo @@ -0,0 +1,22 @@ +/// A type whose instances' values have a standard total ordering +/// +/// `<` is a total ordering; `a == b` implies that `a < b` and `b < a` are both false. +public trait Comparable: Equatable { + + /// Returns `true` iff `self` is ordered before `other`. + fun infix< (_ other: Self) -> Bool + +} + +extension Comparable { + + /// Returns `true` iff `self` is ordered after `other`. + fun infix> (_ other: Self) -> Bool { (other < self) } + + /// Returns `false` iff `self` is ordered after `other`. + fun infix<= (_ other: Self) -> Bool { !(other < self) } + + /// Returns `false` iff `self` is ordered before `other`. + fun infix>= (_ other: Self) -> Bool { !(self < other) } + +} diff --git a/Library/Hylo/Core/Equatable.hylo b/Library/Hylo/Core/Equatable.hylo new file mode 100644 index 000000000..f480af2ae --- /dev/null +++ b/Library/Hylo/Core/Equatable.hylo @@ -0,0 +1,17 @@ +/// A type whose instances' values can be compared for equivalence. +/// +/// `==` is an equivalence relation; i.e. `a == a`, `a == b` ⟺ `b == a`, and `a == b` ∧ `b == c` ⟹ +/// `a == c`. +public trait Equatable { + + /// Returns `true` iff `other` has an equivalent value. + fun infix== (_ other: Self) -> Bool + +} + +extension Equatable { + + /// Returns `false` iff `other` has an equivalent value. + public fun infix!= (_ other: Self) -> Bool { !(self == other) } + +} diff --git a/Library/Hylo/Core/Int.hylo b/Library/Hylo/Core/Int.hylo index 74edcd026..62babb002 100644 --- a/Library/Hylo/Core/Int.hylo +++ b/Library/Hylo/Core/Int.hylo @@ -16,8 +16,8 @@ public type Int { } /// Creates an instance with the same memory representation as `address`. - public init(bit_pattern address: RawPointer) { - &self.value = Builtin.ptrtoint_word(address.value) + public init(bit_pattern address: PointerToMutable) { + &self.value = Builtin.ptrtoint_word(address.base) } /// Returns the absolute value of `self`. diff --git a/Library/Hylo/Core/MemoryAddress.hylo b/Library/Hylo/Core/MemoryAddress.hylo new file mode 100644 index 000000000..23c2187bc --- /dev/null +++ b/Library/Hylo/Core/MemoryAddress.hylo @@ -0,0 +1,13 @@ +// Can't really use this for anything yet due to https://github.com/hylo-lang/hylo/issues/936 +/// A memory address. +typealias MemoryAddress = PointerToMutable + +public extension PointerToMutable where Pointee == Never { + + /// Creates an instance pointing to the same address as `p` + public init(_ p: PointerToMutable) { &self.base = p.base } + + /// Creates an instance pointing to the same address as `p` + public init(_ p: Pointer) { &self.base = p.base } + +} diff --git a/Library/Hylo/Core/MemoryLayout.hylo b/Library/Hylo/Core/MemoryLayout.hylo index 5c308e666..7e241ba9a 100644 --- a/Library/Hylo/Core/MemoryLayout.hylo +++ b/Library/Hylo/Core/MemoryLayout.hylo @@ -4,17 +4,13 @@ public type MemoryLayout { /// The contiguous memory footprint of the `T`'s instances, in bytes. public static fun size() -> Int { let p: Pointer = unsafe_bitcast[T] - return p.with_pointee(fun (_ h) -> Int { - h.size.copy() - }) + return p.unsafe[].size.copy() } /// The preferred memory alignment of the `T`'s instances, in bytes. public static fun alignment() -> Int { let p: Pointer = unsafe_bitcast[T] - return p.with_pointee(fun (_ h) -> Int { - h.alignment.copy() - }) + return p.unsafe[].alignment.copy() } /// The number of bytes from the start of one instance of `T` to the start of the next when diff --git a/Library/Hylo/Core/MutablePointer.hylo b/Library/Hylo/Core/MutablePointer.hylo deleted file mode 100644 index b9df82d4f..000000000 --- a/Library/Hylo/Core/MutablePointer.hylo +++ /dev/null @@ -1,37 +0,0 @@ -/// A pointer for accessing and manipulating typed data. -public type MutablePointer { - - var value: Builtin.ptr - - memberwise init - - /// Creates an instance referencing the same memory as `other`. - public init(mutating other: RawPointer) { - &self.value = other.value - } - - /// Creates a copy of `other`. - public init(_ other: MutableRawPointer) { - &self.value = other.value - } - - /// Creates a null pointer. - public static fun null() -> Self { - .new(value: Builtin.zeroinitializer_ptr()) - } - - /// Returns the result of applying `action` to a projection of the value referenced by `self`. - public fun with_uninitialized_pointee(_ action: [E](set Pointee) -> R) -> R { - action(value as* (remote set Pointee)) - } - - /// Returns the result of applying `action` to a projection of the value referenced by `self`. - public fun with_mutable_pointee(_ action: [E](inout Pointee) -> R) -> R { - action(value as* (remote inout Pointee)) - } - -} - -public conformance MutablePointer: Deinitializable {} - -public conformance MutablePointer: Movable {} diff --git a/Library/Hylo/Core/MutableRawPointer.hylo b/Library/Hylo/Core/MutableRawPointer.hylo deleted file mode 100644 index 91354b3ad..000000000 --- a/Library/Hylo/Core/MutableRawPointer.hylo +++ /dev/null @@ -1,22 +0,0 @@ -/// A raw pointer for accessing and manipulating untyped data. -public type MutableRawPointer { - - var value: Builtin.ptr - - memberwise init - - /// Creates an instance referencing the same memory as `other`. - public init(mutating other: RawPointer) { - &self.value = other.value - } - - /// Creates a null pointer. - public static fun null() -> Self { - .new(value: Builtin.zeroinitializer_ptr()) - } - -} - -public conformance MutableRawPointer: Deinitializable {} - -public conformance MutableRawPointer: Movable {} diff --git a/Library/Hylo/Core/Pointer.hylo b/Library/Hylo/Core/Pointer.hylo index a7b6f37f5..cd58565b8 100644 --- a/Library/Hylo/Core/Pointer.hylo +++ b/Library/Hylo/Core/Pointer.hylo @@ -1,43 +1,57 @@ -/// A pointer for accessing typed data. -public type Pointer { +/// A typed memory address whose contents can be read. +public type Pointer: Regular { - var value: Builtin.ptr + /// The raw bits of the address. + var base: Builtin.ptr memberwise init - /// Creates a copy of `other`. - public init(_ other: RawPointer) { - &self.value = other.value + /// The value at the given address. + /// + /// - Requires: `self` is the address of an object of type `Pointee` and its storage + /// is accessed only through this projection during the projection's lifetime. + public subscript unsafe(): Pointee { + yield base as* (remote let Pointee) } - /// Creates a copy of `other`. - public init(_ other: MutableRawPointer) { - &self.value = other.value + /// Creates an instance representing the same address as `p`. + public init(_ p: PointerToMutable) { + &base = p.base } - /// Creates a null pointer. - public static fun null() -> Self { - .new(value: Builtin.zeroinitializer_ptr()) - } - - /// Returns the result of applying `action` to a projection of the value referenced by `self`. - public fun with_pointee(_ action: [](Pointee) -> T) -> T { - action(value as* (remote let Pointee)) + /// Creates an instance referring to the same address as `p`. + /// + /// Note that dereferencing type punned pointers will cause problems unless the rules for the + /// `usafe[]` subscript (which see) are followed. + public init(type_punning p: Pointer) { + &base = p.base } - /// Projects a pointer to `pointee`. - public static subscript to(_ pointee: Pointee): Self { - Pointer(value: Builtin.address(of: pointee)) + /// Creates an instance that does not address any usable storage. + public static fun null() -> Self { + .new(base: Builtin.zeroinitializer_ptr()) } } -public conformance Pointer: Deinitializable {} - public conformance Pointer: Copyable { public fun copy() -> Self { - Pointer(value: value) + Pointer(base: base) + } + +} + +public conformance Pointer: Equatable { + + /// Returns `true` iff `other` has an equivalent value. + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_ptr(base, other.base)) } } + +/// The address of `x`. +public subscript pointer(to x: T): Pointer { + let { yield Pointer(base: Builtin.address(of: x)) } +} diff --git a/Library/Hylo/Core/PointerToMutable.hylo b/Library/Hylo/Core/PointerToMutable.hylo new file mode 100644 index 000000000..a429a45db --- /dev/null +++ b/Library/Hylo/Core/PointerToMutable.hylo @@ -0,0 +1,77 @@ +/// A typed memory address whose contents can be read and written. +public type PointerToMutable: Regular { + + /// The raw bits of the address. + var base: Builtin.ptr + + memberwise init + + /// The value at the address represented by `self`. + /// + /// - Requires: the address stores an object of type Pointee. + public subscript unsafe(): Pointee { + /// - Requires: nothing mutates the value's storage during this projection's lifetime. + let { yield base as* (remote let Pointee) } + + /// - Requires: the value is mutable, and nothing accesses its + /// storage except through this projection during its lifetime. + inout { yield &(base as* (remote let Pointee)) } + } + + /// Creates an instance with mutable access to the memory pointed to by `p`. + /// + /// - Warning: while this initializer is not in itself unsafe, it should be used with caution. + public init(adding_mutation_to p: Pointer) { + &base = p.base + } + + /// Creates an instance referring to the same address as `p`. + /// + /// Note that dereferencing type-punned pointers will cause problems unless the rules for the + /// `usafe[]` subscript (which see) are followed. + public init(type_punning p: PointerToMutable) { + &base = p.base + } + + /// Creates an instance that does not address any usable storage. + public static fun null() -> Self { + .new(base: Builtin.zeroinitializer_ptr()) + } + + /// Returns the result of applying `initialize` to the uninitialized `Pointee` storage. + /// + /// - Requires: the `MemoryLayout.size()` bytes starting at the address are uninitialized + /// and suitably aligned for `Pointee`. + public fun unsafe_initialize_pointee(_ initialize: inout [E](set Pointee) -> R) -> R { + initialize(base as* (remote set Pointee)) + } + +} + + +// FIXME: rename pointer[toMutable:] +/// The address of `x`. +public subscript pointerToMutable(_ x: inout T): PointerToMutable { + let { yield PointerToMutable(base: Builtin.address(of: x)) } +} + +public conformance PointerToMutable: Copyable { + + /// Returns an equivalent instance. + /// + /// Because we don't have a `nonmutating` keyword yet, writing to the pointee of a `let`-bound + /// `PointerToMutable` is only possible by copying, e.g. `&p.copy().unsafe[] = x`. + public fun copy() -> Self { + PointerToMutable(base: base) + } + +} + +public conformance PointerToMutable: Equatable { + + /// Returns `true` iff `other` has an equivalent value. + public fun infix== (_ other: Self) -> Bool { + Bool(value: Builtin.icmp_eq_ptr(base, other.base)) + } + +} diff --git a/Library/Hylo/Core/RawPointer.hylo b/Library/Hylo/Core/RawPointer.hylo deleted file mode 100644 index 05322b794..000000000 --- a/Library/Hylo/Core/RawPointer.hylo +++ /dev/null @@ -1,52 +0,0 @@ -/// A raw pointer for accessing untyped data. -public type RawPointer { - - var value: Builtin.ptr - - memberwise init - - /// Creates an instance with the bit pattern of `address`. - public init(bit_pattern address: Int) { - &self.value = Builtin.inttoptr_word(address.value) - } - - /// Creates a copy of `other`. - public init(_ other: MutablePointer) { - &self.value = other.value - } - - /// Creates a copy of `other`. - public init(_ other: MutableRawPointer) { - &self.value = other.value - } - - /// Creates a null pointer. - public static fun null() -> Self { - .new(value: Builtin.zeroinitializer_ptr()) - } - -} - -public conformance RawPointer: Deinitializable {} - -public conformance RawPointer: Copyable { - - public fun copy() -> Self { - RawPointer(value: value) - } - -} - -public conformance RawPointer: ForeignConvertible { - - public typealias ForeignRepresentation = Builtin.ptr - - public init(foreign_value: sink Builtin.ptr) { - &self.value = foreign_value - } - - public fun foreign_value() -> Builtin.ptr { - value - } - -} diff --git a/Library/Hylo/Core/Regular.hylo b/Library/Hylo/Core/Regular.hylo new file mode 100644 index 000000000..f679dda7d --- /dev/null +++ b/Library/Hylo/Core/Regular.hylo @@ -0,0 +1,11 @@ +/// Types that can be moved, destroyed, and compared for equality, but might not be copyable +/// (roughly per Stepanov). +/// +/// - Move is value-preserving. +/// - Destruction has no side-effects. +public trait SemiRegular: Deinitializable, Movable, Equatable {} + +/// Regular types (roughly per Stepanov). +/// +/// Copies have equal value. +public trait Regular: Deinitializable, Copyable, Movable, Equatable {} diff --git a/Library/Hylo/Core/Runtime.hylo b/Library/Hylo/Core/Runtime.hylo index 342cc790a..9ff3f7ad3 100644 --- a/Library/Hylo/Core/Runtime.hylo +++ b/Library/Hylo/Core/Runtime.hylo @@ -17,7 +17,7 @@ public namespace Runtime { let alignment: Int /// The description of the type's layout. - let representation: RawPointer + let representation: PointerToMutable } diff --git a/Library/Hylo/Core/String.hylo b/Library/Hylo/Core/String.hylo index 940dcf4e5..4d84a9da1 100644 --- a/Library/Hylo/Core/String.hylo +++ b/Library/Hylo/Core/String.hylo @@ -5,7 +5,7 @@ public type String { public var size: Int /// The contents of the string, encoded in UTF8. - public var utf8: RawPointer + public var utf8: PointerToMutable memberwise init diff --git a/Library/Hylo/Core/UInt.hylo b/Library/Hylo/Core/UInt.hylo index 7d9d49ddd..6a2dca060 100644 --- a/Library/Hylo/Core/UInt.hylo +++ b/Library/Hylo/Core/UInt.hylo @@ -16,8 +16,8 @@ public type UInt { } /// Creates an instance with the same memory representation as `address`. - public init(bit_pattern address: RawPointer) { - &self.value = Builtin.ptrtoint_word(address.value) + public init(bit_pattern address: PointerToMutable) { + &self.value = Builtin.ptrtoint_word(address.base) } /// Returns `true` if `self` is equal to `other`. Otherwise, returns `false`. diff --git a/Library/Hylo/DynamicBuffer.hylo b/Library/Hylo/DynamicBuffer.hylo index 353957b08..d1c8cbbda 100644 --- a/Library/Hylo/DynamicBuffer.hylo +++ b/Library/Hylo/DynamicBuffer.hylo @@ -1,6 +1,6 @@ /// A buffer of elements stored in a contiguous region of memory, whose size is determined at /// instance creation. -public type DynamicBuffer: Deinitializable { +public type DynamicBuffer: Deinitializable { /// The description of payloads in `DynamicBuffer`s. typealias BufferHeader = { @@ -12,7 +12,7 @@ public type DynamicBuffer: Deinitializable { static fun payload_offset() -> Int { let a = MemoryLayout.alignment() let s = MemoryLayout.size() - return s + (a - s % a) % a + return s + (a - s % a) } /// A pointer to the base of the buffer's out-of-line storage. @@ -20,7 +20,7 @@ public type DynamicBuffer: Deinitializable { /// A null value denotes an empty buffer. Otherwise, `storage` points at an instance of /// `BufferHeader` that is followed by a region capable of storing at most `self.capacity` /// instances of `Element`, offset to satisfy `Element`'s alignment requirement'. - var storage: MutableRawPointer + var storage: PointerToMutable /// Creates an empty instance. public init() { @@ -33,15 +33,9 @@ public type DynamicBuffer: Deinitializable { capacity: Int, initializing_header_with init_header: [E](set Header) -> Void ) { - &storage = .allocate( + &storage = .allocate_bytes( count: Self.payload_offset() + capacity * MemoryLayout.stride(), aligned_at: MemoryLayout.alignment()) - - let p = MutablePointer(storage) - p.with_uninitialized_pointee(fun (_ h: set BufferHeader) -> Void { - &h.0 = capacity.copy() - init_header(&h.1) - }) } /// Deinitializes `self`. @@ -51,10 +45,8 @@ public type DynamicBuffer: Deinitializable { /// The number of elements that can be stored in `self`. public fun capacity() -> Int { - let p = Pointer(storage) - return p.with_pointee(fun (_ h: BufferHeader) -> Int { - h.0.copy() - }) + let p = PointerToMutable(type_punning: storage) + return p.unsafe[].0.copy() } } diff --git a/Library/Hylo/LibC.hylo b/Library/Hylo/LibC.hylo index 1027358ec..6657da845 100644 --- a/Library/Hylo/LibC.hylo +++ b/Library/Hylo/LibC.hylo @@ -5,20 +5,20 @@ /// the implementation. /// - size: The number of bytes to allocate. Must be an integral multiple of `alignment`. @ffi("aligned_alloc") -fun aligned_alloc(_ alignment: Int, _ size: Int) -> RawPointer +fun aligned_alloc(_ alignment: Int, _ size: Int) -> CVoidPointer /// Deallocates the block of memory pointed at by `pointer`. @ffi("free") -fun free(_ pointer: RawPointer) +fun free(_ pointer: CVoidPointer) /// Terminates the program abnormally. @ffi("abort") public fun fatal_error() -> Never @ffi("fdopen") -public fun fdopen(_ descriptor: Int, _ mode: RawPointer) -> RawPointer +public fun fdopen(_ descriptor: Int, _ mode: CVoidPointer) -> CVoidPointer -/// Writes to `stream` the contents of `data`, which containts `count` elements of `size` bytes, +/// Writes to `stream` the contents of `data`, which contains `count` elements of `size` bytes, /// returning the number of elements written. @ffi("fwrite") -public fun fwrite(_ data: RawPointer, _ size: Int, _ count: Int, _ stream: RawPointer) -> Int +public fun fwrite(_ data: CVoidPointer, _ size: Int, _ count: Int, _ stream: CVoidPointer) -> Int diff --git a/Library/Hylo/Pointers+DynamicAllocation.hylo b/Library/Hylo/Pointers+DynamicAllocation.hylo index 36c0eb709..b5872f1b1 100644 --- a/Library/Hylo/Pointers+DynamicAllocation.hylo +++ b/Library/Hylo/Pointers+DynamicAllocation.hylo @@ -1,29 +1,22 @@ -public extension MutableRawPointer { +public extension PointerToMutable where Pointee == Never { /// Allocates memory for `count` bytes at given `alignment`. - public static fun allocate(count: Int, aligned_at alignment: Int) -> Self { - .new(mutating: aligned_alloc(alignment, count)) - } - - /// Deallocates the memory previously allocated at `self`. - public fun deallocate() { - free(RawPointer(self)) + public static fun allocate_bytes(count: Int, aligned_at alignment: Int) -> Self { + Self.new(base: aligned_alloc(alignment, count).base) } } -public extension MutablePointer { +public extension PointerToMutable { /// Allocates memory for `count` instances of `Pointee`. public static fun allocate(count: Int) -> Self { - .new(MutableRawPointer.allocate( - count: MemoryLayout.stride(), - aligned_at: MemoryLayout.alignment())) + Self.new(base: aligned_alloc(MemoryLayout.alignment(), MemoryLayout.stride() * count).base) } /// Deallocates the memory previously allocated at `self`. public fun deallocate() { - free(RawPointer(self)) + free(CVoidPointer(base: base)) } } diff --git a/Library/Hylo/Print.hylo b/Library/Hylo/Print.hylo index c06a670bd..5265d4678 100644 --- a/Library/Hylo/Print.hylo +++ b/Library/Hylo/Print.hylo @@ -1,11 +1,11 @@ /// Writes the textual representation of `item` to the standard output. public fun print(_ item: String, terminator: String = "\n") { let stream = stdout() - _ = fwrite(item.utf8, 1, item.size, stream) - _ = fwrite(terminator.utf8, 1, 1, stream) + _ = fwrite(CVoidPointer(base: item.utf8.base), 1, item.size, CVoidPointer(base: stream.base)) + _ = fwrite(CVoidPointer(base: terminator.utf8.base), 1, 1, CVoidPointer(base: stream.base)) } /// The standard output of the current process. -fun stdout() -> RawPointer { - fdopen(1, "w".utf8) +fun stdout() -> PointerToMutable { + .new(base: fdopen(1, CVoidPointer(base: "w".utf8.base)).base) } diff --git a/Library/Library.swift b/Library/Library.swift index c2f7d8459..6b35fa3ed 100644 --- a/Library/Library.swift +++ b/Library/Library.swift @@ -1,7 +1,9 @@ -import class Foundation.Bundle +import Foundation + +fileprivate let libraryRoot = URL(fileURLWithPath: #filePath).deletingLastPathComponent() /// The root URL of Hylo's core library. -public let core = Bundle.module.url(forResource: "Hylo/Core", withExtension: nil) +public let core = libraryRoot.appendingPathComponent("Hylo/Core") /// The root URL of Hylo's standard library. -public let standardLibrary = Bundle.module.url(forResource: "Hylo", withExtension: nil) +public let standardLibrary = libraryRoot.appendingPathComponent("Hylo") diff --git a/Package.swift b/Package.swift index 2162ef198..eaf0436d1 100644 --- a/Package.swift +++ b/Package.swift @@ -106,7 +106,7 @@ let package = Package( .target( name: "TestUtils", - dependencies: ["Core", "Utils"], + dependencies: ["Core", "Driver", "Utils"], swiftSettings: allTargetsSwiftSettings), .target( @@ -133,6 +133,11 @@ let package = Package( dependencies: ["Utils"], swiftSettings: allTargetsSwiftSettings), + .testTarget( + name: "DriverTests", + dependencies: ["Driver"], + swiftSettings: allTargetsSwiftSettings), + .testTarget( name: "ManglingTests", dependencies: ["Core", "FrontEnd", "IR", "TestUtils"], @@ -145,12 +150,13 @@ let package = Package( plugins: ["TestGeneratorPlugin"]), .testTarget( - name: "DriverTests", - dependencies: ["Driver"], - swiftSettings: allTargetsSwiftSettings), + name: "EndToEndTests", + dependencies: ["Driver", "TestUtils"], + swiftSettings: allTargetsSwiftSettings, + plugins: ["TestGeneratorPlugin"]), .testTarget( - name: "EndToEndTests", + name: "LibraryTests", dependencies: ["Driver", "TestUtils"], swiftSettings: allTargetsSwiftSettings, plugins: ["TestGeneratorPlugin"]), diff --git a/README.md b/README.md index 37320ad66..6481baa50 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,9 @@ Please visit our [website](https://hylo-lang.org) to get more information about This project is written in [Swift](https://swift.org) and distributed in the form of a package, built with [Swift Package Manager](https://swift.org/package-manager/). You will need Swift 5.7 or higher to build the compiler from sources. -*Note to Windows users: although this project is **not** Unix-specific, Windows support is not guaranteed due to the instability of continuous integration (see https://github.com/hylo-lang/hylo/issues/252 and https://github.com/hylo-lang/hylo/issues/805).* +**Windows users:** +1. although this project is **not** Unix-specific, Windows support is not guaranteed due to the instability of continuous integration (see [issue 252](https://github.com/hylo-lang/hylo/issues/252) and [issue 805](https://github.com/hylo-lang/hylo/issues/805). +2. This repository contains symbolic links, so you'll need to [enable support](https://stackoverflow.com/questions/5917249/git-symbolic-links-in-windows/59761201#59761201) for them before checking it out. ### Prerequisites @@ -62,7 +64,7 @@ That integrated terminal is connected to the Devcontainer, as if by ssh. Use the `make-pkgconfig` tool to configure LLVM's library description (see steps 3 in [prerequisites](#prerequisites)). You can now run `swift test -c release` to build and test for Linux. -The Hylo repository files are mounted into the container, so any changes made locally (in VSCode or in other editors) will be automatically propagated into the Devcontainer. However, if you need to modifiy any of the files in the `.devcontainer` directory, you will need to rebuild the container with `> Dev Containers: Rebuild and Reopen in Container`. +The Hylo repository files are mounted into the container, so any changes made locally (in VSCode or in other editors) will be automatically propagated into the Devcontainer. However, if you need to modify any of the files in the `.devcontainer` directory, you will need to rebuild the container with `> Dev Containers: Rebuild and Reopen in Container`. ## Implementation status diff --git a/Sources/CLI/FullPathInFatalErrors.swift b/Sources/CLI/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/CLI/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/CodeGen/FullPathInFatalErrors.swift b/Sources/CodeGen/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/CodeGen/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/CodeGen/LLVM/LLVMProgram.swift b/Sources/CodeGen/LLVM/LLVMProgram.swift index a5c5e0d06..b422143a2 100644 --- a/Sources/CodeGen/LLVM/LLVMProgram.swift +++ b/Sources/CodeGen/LLVM/LLVMProgram.swift @@ -35,7 +35,7 @@ public struct LLVMProgram { } } - /// Applies the mandatory IR simplication passes on each module in `self`. + /// Applies the mandatory IR simplification passes on each module in `self`. public mutating func applyMandatoryPasses() { for k in llvmModules.keys { llvmModules[k]!.runDefaultModulePasses(optimization: .none, for: target) diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 7d7fe8e3b..4c15610ba 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -166,7 +166,7 @@ extension LLVM.Module { ) -> LLVM.IRValue { switch c { case let v as IR.IntegerConstant: - guard v.value.bitWidth <= 64 else { fatalError("not implemented") } + guard v.value.bitWidth <= 64 else { UNIMPLEMENTED() } let t = LLVM.IntegerType(v.value.bitWidth, in: &self) return t.constant(v.value.words[0]) @@ -276,7 +276,7 @@ extension LLVM.Module { case .function(let f): return declare(f, from: ir) case .value: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -290,7 +290,7 @@ extension LLVM.Module { case let u as TupleType: return transpiledMetatype(of: u, usedIn: m, from: ir) default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -608,7 +608,7 @@ extension LLVM.Module { case is IR.Yield: insert(yield: i) default: - fatalError("not implemented") + UNIMPLEMENTED() } } diff --git a/Sources/Core/AST/AST.swift b/Sources/Core/AST/AST.swift index 336ca1cb5..ec694acfd 100644 --- a/Sources/Core/AST/AST.swift +++ b/Sources/Core/AST/AST.swift @@ -1,4 +1,3 @@ -import Foundation import Utils /// An abstract syntax tree. diff --git a/Sources/Core/AST/BundledNode.swift b/Sources/Core/AST/BundledNode.swift index 12d09c98a..188b6b6a2 100644 --- a/Sources/Core/AST/BundledNode.swift +++ b/Sources/Core/AST/BundledNode.swift @@ -23,7 +23,7 @@ public struct BundledNode { /// The scope in which the node resides. /// - /// - Requres: `self` does not identify a module. + /// - Requires: `self` does not identify a module. public var scope: AnyScopeID { container.nodeToScope[id]! } diff --git a/Sources/Core/AST/Expr/RemoteExpr.swift b/Sources/Core/AST/Expr/RemoteExpr.swift index 755304ef3..4b2731fbe 100644 --- a/Sources/Core/AST/Expr/RemoteExpr.swift +++ b/Sources/Core/AST/Expr/RemoteExpr.swift @@ -12,7 +12,7 @@ public struct RemoteExpr: Expr { /// The expression of the projected type. public let operand: AnyExprID - /// Creates an isntance with the given properties. + /// Creates an instance with the given properties. public init( introducerSite: SourceRange, convention: SourceRepresentable, diff --git a/Sources/Core/BuiltinFunction.swift b/Sources/Core/BuiltinFunction.swift index 4978fb0ed..eae88053d 100644 --- a/Sources/Core/BuiltinFunction.swift +++ b/Sources/Core/BuiltinFunction.swift @@ -31,8 +31,10 @@ extension BuiltinFunction { /// `Builtin.address(of v: T) -> Builtin.ptr` /// - /// Returns a pointer to a value passed as a `let` or `inout` argument. The returned pointer - /// is only valid within the function in which `address_of` is being called. + /// Returns a pointer to the storage of the argument. + /// + /// The resulting pointer is dereferenceable only for the lifetime of the argument; additional + /// measures may be needed to keep the argument alive during the pointer's use. case addressOf } @@ -320,7 +322,7 @@ private func mathFlags(_ stream: inout ArraySlice) -> NativeInstructi return result } -/// Returns an overlflow behavior parsed from `stream` or `.ignore` if none can be parsed. +/// Returns an overflow behavior parsed from `stream` or `.ignore` if none can be parsed. private func overflowBehavior(_ stream: inout ArraySlice) -> LLVM.OverflowBehavior { switch stream.first { case "nuw": diff --git a/Sources/Core/CompileTimeValues/GenericArguments.swift b/Sources/Core/CompileTimeValues/GenericArguments.swift index db5d37093..a7a772a60 100644 --- a/Sources/Core/CompileTimeValues/GenericArguments.swift +++ b/Sources/Core/CompileTimeValues/GenericArguments.swift @@ -69,7 +69,7 @@ public struct GenericArguments { /// /// - Requires: `self` does not define a value for any of the values defined in `suffix`. public mutating func append(_ suffix: Self) { - // Note: `merging` perserves order. + // Note: `merging` preserves order. contents.merge(suffix.contents, uniquingKeysWith: { (_, _) in unreachable() }) } diff --git a/Sources/Core/DiagnosticSet.swift b/Sources/Core/DiagnosticSet.swift index a9f61af5d..d08ce0625 100644 --- a/Sources/Core/DiagnosticSet.swift +++ b/Sources/Core/DiagnosticSet.swift @@ -1,5 +1,5 @@ /// A set of `Diagnostic` that can answer the question “was there an error?” in O(1). -public struct DiagnosticSet { +public struct DiagnosticSet: Error { /// The elements of `self`. public private(set) var elements: Set = [] @@ -59,10 +59,4 @@ extension DiagnosticSet: CustomStringConvertible { } -extension DiagnosticSet: Error { - - var localizedDescription: String { description } - -} - extension DiagnosticSet: Equatable {} diff --git a/Sources/Core/FullPathInFatalErrors.swift b/Sources/Core/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/Core/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/Core/Types/AnyType.swift b/Sources/Core/Types/AnyType.swift index ebdb91801..b25be1a3a 100644 --- a/Sources/Core/Types/AnyType.swift +++ b/Sources/Core/Types/AnyType.swift @@ -100,7 +100,7 @@ public struct AnyType: TypeProtocol { /// `self` transformed as the type of a member of `receiver`, which is existential. public func asMember(of receiver: ExistentialType) -> AnyType { - let m = LambdaType(self) ?? fatalError("not implemented") + let m = LambdaType(self) ?? UNIMPLEMENTED() return ^m.asMember(of: receiver) } diff --git a/Sources/Core/Types/BoundGenericType.swift b/Sources/Core/Types/BoundGenericType.swift index ad046cd8d..74d49e4f2 100644 --- a/Sources/Core/Types/BoundGenericType.swift +++ b/Sources/Core/Types/BoundGenericType.swift @@ -24,7 +24,7 @@ public struct BoundGenericType: TypeProtocol { if let t = a as? AnyType { flags.merge(t.flags) } else { - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -51,7 +51,7 @@ public struct BoundGenericType: TypeProtocol { if let t = a as? AnyType { return t.transform(mutating: &m, transformer) } else { - fatalError("not implemented") + UNIMPLEMENTED() } })) } diff --git a/Sources/Core/Types/ExistentialType.swift b/Sources/Core/Types/ExistentialType.swift index 2e1294e16..6e12804bf 100644 --- a/Sources/Core/Types/ExistentialType.swift +++ b/Sources/Core/Types/ExistentialType.swift @@ -44,7 +44,7 @@ public struct ExistentialType: TypeProtocol { self.interface = interface self.constraints = constraints - // TODO: Consider the flags of the types in the cosntraints? + // TODO: Consider the flags of the types in the constraints? self.flags = interface.flags } diff --git a/Sources/Core/Types/ParameterType.swift b/Sources/Core/Types/ParameterType.swift index d69bb8a98..b502d7e99 100644 --- a/Sources/Core/Types/ParameterType.swift +++ b/Sources/Core/Types/ParameterType.swift @@ -1,6 +1,6 @@ import Utils -/// The type of a paramter in a lambda, method, or subscript type. +/// The type of a parameter in a lambda, method, or subscript type. public struct ParameterType: TypeProtocol { /// The passing convention of the parameter. diff --git a/Sources/Core/Types/TypeFlags.swift b/Sources/Core/Types/TypeFlags.swift index 4205f41b8..4c88e12d0 100644 --- a/Sources/Core/Types/TypeFlags.swift +++ b/Sources/Core/Types/TypeFlags.swift @@ -35,7 +35,7 @@ public struct TypeFlags: Hashable { existential = existential | flags.existential } - /// Retuns a set of flags in which `flags` have been inserted. + /// Returns a set of flags in which `flags` have been inserted. public func inserting(_ flags: TypeFlags) -> TypeFlags { var newFlags = self newFlags.insert(flags) @@ -48,7 +48,7 @@ public struct TypeFlags: Hashable { existential = existential & ~flags.existential } - /// Retuns a set of flags in which `flags` have been removed. + /// Returns a set of flags in which `flags` have been removed. public func removing(_ flags: TypeFlags) -> TypeFlags { var newFlags = self newFlags.remove(flags) @@ -61,7 +61,7 @@ public struct TypeFlags: Hashable { existential = existential | flags.existential } - /// Retuns a set of flags in which `flags` have been merged. + /// Returns a set of flags in which `flags` have been merged. public func merging(_ flags: TypeFlags) -> TypeFlags { var newFlags = self newFlags.merge(flags) diff --git a/Sources/Core/UnexpectedNode.swift b/Sources/Core/UnexpectedNode.swift index 953d90ab4..c56f09da8 100644 --- a/Sources/Core/UnexpectedNode.swift +++ b/Sources/Core/UnexpectedNode.swift @@ -1,7 +1,7 @@ /// Causes a fatal error reporting an unexpected `n`, where `n` is a node in `nodes`. public func unexpected( _ n: ID, in nodes: AST, - file: StaticString = #file, line: UInt = #line + file: StaticString = #filePath, line: UInt = #line ) -> Never { fatalError("unexpected \(n.kind) at location: \(nodes[n].site.first())", file: file, line: line) } diff --git a/Sources/Core/Unimplemented.swift b/Sources/Core/Unimplemented.swift new file mode 100644 index 000000000..586375c52 --- /dev/null +++ b/Sources/Core/Unimplemented.swift @@ -0,0 +1,9 @@ +/// Stops the program, indicating that source program uses a feature that has not yet been +/// implemented. +public func UNIMPLEMENTED( + _ message: @autoclosure () -> String = "not implemented", + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + fatalError(message(), file: file, line: line) +} diff --git a/Sources/Driver/FullPathInFatalErrors.swift b/Sources/Driver/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/Driver/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/Driver/HyloCommand.swift b/Sources/Driver/HyloCommand.swift index ad7a7648d..58081cebb 100644 --- a/Sources/Driver/HyloCommand.swift +++ b/Sources/Driver/HyloCommand.swift @@ -225,11 +225,11 @@ public struct Driver: ParsableCommand { #else _ = objectFiles _ = binaryPath - fatalError("not implemented") + UNIMPLEMENTED() #endif } - /// Returns `true` if type inference related to `n`, which is in `p`, whould be traced. + /// Returns `true` if type inference related to `n`, which is in `p`, would be traced. private func shouldTraceInference(_ n: AnyNodeID, _ p: TypedProgram) -> Bool { if let s = inferenceTracingSite { return s.bounds.contains(p[n].site.first()) @@ -239,7 +239,7 @@ public struct Driver: ParsableCommand { } /// Returns `program` lowered to Hylo IR, accumulating diagnostics in `log` and throwing if an - /// error occured. + /// error occurred. /// /// Mandatory IR passes are applied unless `self.outputType` is `.rawIR`. private func lower( @@ -258,7 +258,7 @@ public struct Driver: ParsableCommand { } /// Returns `m`, which is `program`, lowered to Hylo IR, accumulating diagnostics in `log` and - /// throwing if an error occured. + /// throwing if an error occurred. /// /// Mandatory IR passes are applied unless `self.outputType` is `.rawIR`. private func lower( @@ -360,7 +360,7 @@ public struct Driver: ParsableCommand { /// /// - Requires: `url` must denote a directly. private func addModule(url: URL) { - fatalError("not implemented") + UNIMPLEMENTED() } /// Returns the path of the specified executable. diff --git a/Sources/FrontEnd/AST+Extensions.swift b/Sources/FrontEnd/AST+Extensions.swift index 87c58472d..f96e2c5a0 100644 --- a/Sources/FrontEnd/AST+Extensions.swift +++ b/Sources/FrontEnd/AST+Extensions.swift @@ -6,10 +6,10 @@ import Utils extension AST { /// An instance that includes just the core module. - public static var coreModule = AST(libraryRoot: HyloModule.core!) + public static var coreModule = AST(libraryRoot: HyloModule.core) /// An instance that includes just the standard library. - public static var standardLibrary = AST(libraryRoot: HyloModule.standardLibrary!) + public static var standardLibrary = AST(libraryRoot: HyloModule.standardLibrary) /// Creates an instance that includes the Hylo library rooted at `libraryRoot`. private init(libraryRoot: URL) { @@ -23,7 +23,7 @@ extension AST { diagnostics: &diagnostics) assert(isCoreModuleLoaded) } catch let error { - fatalError("Error parsing the Hylo module:\n\(error.localizedDescription)") + fatalError("Error parsing the Hylo module:\n\(error)") } } diff --git a/Sources/FrontEnd/AST+UseCollection.swift b/Sources/FrontEnd/AST+UseCollection.swift index 468285a53..b3b1b3ff5 100644 --- a/Sources/FrontEnd/AST+UseCollection.swift +++ b/Sources/FrontEnd/AST+UseCollection.swift @@ -22,7 +22,7 @@ private struct UseVisitor: ASTWalkObserver { private(set) var uses: [(NameExpr.ID, AccessEffect)] = [] /// Records a use of `n` that with mutability `k`. - private mutating func recordOccurence(_ k: AccessEffect, _ n: NameExpr.ID) { + private mutating func recordOccurrence(_ k: AccessEffect, _ n: NameExpr.ID) { uses.append((n, k)) } @@ -55,7 +55,7 @@ private struct UseVisitor: ASTWalkObserver { private mutating func visit(inoutExpr e: InoutExpr.ID, in ast: AST) -> Bool { if let x = root(ast[e].subject, in: ast) { - recordOccurence(.inout, x) + recordOccurrence(.inout, x) return false } else { return true @@ -64,7 +64,7 @@ private struct UseVisitor: ASTWalkObserver { private mutating func visit(nameExpr e: NameExpr.ID, in ast: AST) -> Bool { if ast[e].domain == .none { - recordOccurence(.let, e) + recordOccurrence(.let, e) return false } else { return true diff --git a/Sources/FrontEnd/FullPathInFatalErrors.swift b/Sources/FrontEnd/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/FrontEnd/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/FrontEnd/Parse/Parser.swift b/Sources/FrontEnd/Parse/Parser.swift index 5d6eebc0f..32ebe3c97 100644 --- a/Sources/FrontEnd/Parse/Parser.swift +++ b/Sources/FrontEnd/Parse/Parser.swift @@ -55,7 +55,7 @@ public enum Parser { state.diagnostics.insert( Diagnostic( level: .error, - message: error.localizedDescription, + message: "\(type(of: error)): \(error)", site: state.lexer.sourceCode.range(startIndex ..< state.currentIndex))) continue } @@ -1682,7 +1682,7 @@ public enum Parser { case .remote: // Remote type expression. - return try parseRemotExpr(in: &state).map(AnyExprID.init) + return try parseRemoteExpr(in: &state).map(AnyExprID.init) case .spawn: // Spawn expression. @@ -2072,7 +2072,7 @@ public enum Parser { return .block(s) } - private static func parseRemotExpr( + private static func parseRemoteExpr( in state: inout ParserState ) throws -> RemoteExpr.ID? { guard let introducer = state.take(.remote) else { return nil } @@ -2184,7 +2184,7 @@ public enum Parser { guard let opener = state.take(.lBrack) else { return nil } // Parse the environment, if any. - let environement = try parseExpr(in: &state) + let environment = try parseExpr(in: &state) // If we don't find the closing bracket, backtrack and parse a compound literal. if state.take(.rBrack) == nil { @@ -2196,7 +2196,7 @@ public enum Parser { if !state.isNext(.lParen) { let expr = state.insert( BufferLiteralExpr( - elements: environement != nil ? [environement!] : [], + elements: environment != nil ? [environment!] : [], site: state.range(from: opener.site.start))) return AnyExprID(expr) } @@ -2223,7 +2223,7 @@ public enum Parser { // Synthesize the environment as an empty tuple if we parsed `[]`. let s = state.lexer.sourceCode.emptyRange(at: opener.site.start) - let e = environement ?? AnyExprID(state.insert(TupleTypeExpr(elements: [], site: s))) + let e = environment ?? AnyExprID(state.insert(TupleTypeExpr(elements: [], site: s))) let expr = state.insert( LambdaTypeExpr( @@ -3054,7 +3054,7 @@ public enum Parser { } -/// The attributes and modifiers preceeding a declaration. +/// The attributes and modifiers preceding a declaration. struct DeclPrologue { /// Indicates whether the prologue is empty. @@ -3364,7 +3364,7 @@ private func inContext( WrapInContext(context: context, base: base) } -/// Creates a combinator that applies `base` only if its input is not preceeded by whitespaces. +/// Creates a combinator that applies `base` only if its input is not preceded by whitespaces. private func withoutLeadingWhitespace( _ base: Base ) -> Apply @@ -3372,7 +3372,7 @@ where Base.Context == ParserState { Apply({ (state) in try state.hasLeadingWhitespace ? nil : base.parse(&state) }) } -/// Creates a combinator that applies `base` only if its input is not preceeded by newlines. +/// Creates a combinator that applies `base` only if its input is not preceded by newlines. private func onSameLine( _ base: Base ) -> Apply diff --git a/Sources/FrontEnd/Parse/ParserState.swift b/Sources/FrontEnd/Parse/ParserState.swift index 65164c7b8..d15def9f9 100644 --- a/Sources/FrontEnd/Parse/ParserState.swift +++ b/Sources/FrontEnd/Parse/ParserState.swift @@ -201,7 +201,7 @@ struct ParserState { } /// Consumes and returns the next token, only if it has the specified kind and if it is not - /// preceeded by any whitespace. + /// preceded by any whitespace. mutating func takeWithoutSkippingWhitespace(_ kind: Token.Kind) -> Token? { if hasLeadingWhitespace { return nil } return take(kind) diff --git a/Sources/FrontEnd/Parse/Token.swift b/Sources/FrontEnd/Parse/Token.swift index af23e4d91..58d53e1b7 100644 --- a/Sources/FrontEnd/Parse/Token.swift +++ b/Sources/FrontEnd/Parse/Token.swift @@ -148,7 +148,7 @@ public struct Token { } } - /// Indicates whether `self` may be at the begining of a declaration. + /// Indicates whether `self` may be at the beginning of a declaration. public var mayBeginDecl: Bool { switch kind { case .`conformance`, @@ -177,7 +177,7 @@ public struct Token { } } - /// Indicates whether `self` may be at the begining of a control statement. + /// Indicates whether `self` may be at the beginning of a control statement. public var mayBeginCtrlStmt: Bool { switch kind { case .break, .continue, .for, .if, .lBrace, .return, .while: diff --git a/Sources/FrontEnd/TypeChecking/ConstraintSystem.swift b/Sources/FrontEnd/TypeChecking/ConstraintSystem.swift index f35c31bb6..bdd280a76 100644 --- a/Sources/FrontEnd/TypeChecking/ConstraintSystem.swift +++ b/Sources/FrontEnd/TypeChecking/ConstraintSystem.swift @@ -194,9 +194,9 @@ struct ConstraintSystem { // Process structural and built-in conformances. switch goal.concept { case checker.program.ast.movableTrait: - return goal.model.isBuiltin ? .success : solve(structualConformance: goal) + return goal.model.isBuiltin ? .success : solve(structuralConformance: goal) case checker.program.ast.deinitializableTrait: - return goal.model.isBuiltin ? .success : solve(structualConformance: goal) + return goal.model.isBuiltin ? .success : solve(structuralConformance: goal) case checker.program.ast.foreignConvertibleTrait: return goal.model.isBuiltin ? .success : .failure(failureToSolve(goal)) default: @@ -209,14 +209,14 @@ struct ConstraintSystem { /// if `goal.model` isn't a structural type. /// /// - Requires: `goal.model` is not a type variable. - private mutating func solve(structualConformance goal: ConformanceConstraint) -> Outcome { + private mutating func solve(structuralConformance goal: ConformanceConstraint) -> Outcome { assert(!goal.model.isTypeVariable) switch goal.model.base { case let t as TupleType: - return delegate(structualConformance: goal, for: t.elements.lazy.map(\.type)) + return delegate(structuralConformance: goal, for: t.elements.lazy.map(\.type)) case let t as UnionType: - return delegate(structualConformance: goal, for: t.elements) + return delegate(structuralConformance: goal, for: t.elements) default: return .failure(failureToSolve(goal)) } @@ -225,7 +225,7 @@ struct ConstraintSystem { /// Returns the outcome of breaking down `goal`, which is a structural conformance constraint, /// into a set containing a conformance constraint for each type in `elements`. private mutating func delegate>( - structualConformance goal: ConformanceConstraint, for elements: S + structuralConformance goal: ConformanceConstraint, for elements: S ) -> Outcome { if elements.isEmpty { return .success @@ -366,7 +366,7 @@ struct ConstraintSystem { return delegate(to: [s]) case (_, let r as ExistentialType): - guard r.constraints.isEmpty else { fatalError("not implemented") } + guard r.constraints.isEmpty else { UNIMPLEMENTED() } // Penalize type coercion. penalties += 1 @@ -474,7 +474,7 @@ struct ConstraintSystem { return .product([s], failureToSolve(goal)) } - /// Returns a clousre diagnosing a failure to solve `goal`. + /// Returns a closure diagnosing a failure to solve `goal`. private mutating func failureToSolve(_ goal: SubtypingConstraint) -> DiagnoseFailure { { (d, m, _) in let (l, r) = (m.reify(goal.left), m.reify(goal.right)) @@ -604,7 +604,7 @@ struct ConstraintSystem { return delegate(to: [s]) default: - // TODO: Handle bound generic typess + // TODO: Handle bound generic types break } @@ -649,7 +649,7 @@ struct ConstraintSystem { return delegate(to: subordinates) } - /// Returns a clousre diagnosing a failure to solve `g` because of an invalid callee. + /// Returns a closure diagnosing a failure to solve `g` because of an invalid callee. private mutating func invalidCallee(_ g: CallConstraint) -> DiagnoseFailure { { (d, m, _) in if g.isArrow { @@ -746,7 +746,7 @@ struct ConstraintSystem { /// systems configured with `configureSubSystem` returns the best solution of each exploration. /// /// - Parameters: - /// - g: the goal whose choices whould be explored; must denote a constraint of type `T`. + /// - g: the goal whose choices would be explored; must denote a constraint of type `T`. /// - checker: an instance used to query type relations and resolve names. /// - configureSubSystem: A closure that prepares a constraint system using one of `g`'s /// choices and returns the identities of the goals added to that system. diff --git a/Sources/FrontEnd/TypeChecking/NameResolutionContext.swift b/Sources/FrontEnd/TypeChecking/NameResolutionContext.swift index a38e607ac..c982ffbf9 100644 --- a/Sources/FrontEnd/TypeChecking/NameResolutionContext.swift +++ b/Sources/FrontEnd/TypeChecking/NameResolutionContext.swift @@ -3,7 +3,7 @@ import Core /// The context in which a component of a name expression gets resolved. /// /// This structure is used during name resolution to identify the type of which an entity is member -/// and the generic arguments captured by that entity. The followin invariants are maintained: +/// and the generic arguments captured by that entity. The following invariants are maintained: /// - `arguments` contains the arguments of `type`'s generic parameters (if any) and the arguments /// to generic parameters captured by the scope in which name resolution takes place. /// - if `type` is a bound generic type, `type.arguments[k] = arguments[k]` for all keys in `type`. diff --git a/Sources/FrontEnd/TypeChecking/NameResolutionResult.swift b/Sources/FrontEnd/TypeChecking/NameResolutionResult.swift index 8732259fe..b860e8bf8 100644 --- a/Sources/FrontEnd/TypeChecking/NameResolutionResult.swift +++ b/Sources/FrontEnd/TypeChecking/NameResolutionResult.swift @@ -14,7 +14,7 @@ enum NameResolutionResult { /// Name resolution couldn't complete because the first component of the expression couldn't be /// resolved without type inference. The payload contains the type of the resolved part, unless - /// it was left implicit, and the remaning unresolved components. + /// it was left implicit, and the remaining unresolved components. /// /// - Invariant: `components` is not empty. case canceled(AnyType?, _ components: [NameExpr.ID]) diff --git a/Sources/FrontEnd/TypeChecking/Solution.swift b/Sources/FrontEnd/TypeChecking/Solution.swift index 935445ba0..6219069f2 100644 --- a/Sources/FrontEnd/TypeChecking/Solution.swift +++ b/Sources/FrontEnd/TypeChecking/Solution.swift @@ -98,7 +98,7 @@ extension Solution: CustomStringConvertible { result.append(" \(k) : \(v)\n") } - result.append("penalities: \(penalties)\n") + result.append("penalties: \(penalties)\n") result.append("diagnostics: \(diagnostics.elements.count)\n") diff --git a/Sources/FrontEnd/TypeChecking/SubstitutionMap.swift b/Sources/FrontEnd/TypeChecking/SubstitutionMap.swift index 749d36823..e4a7fa1eb 100644 --- a/Sources/FrontEnd/TypeChecking/SubstitutionMap.swift +++ b/Sources/FrontEnd/TypeChecking/SubstitutionMap.swift @@ -66,7 +66,7 @@ struct SubstitutionMap { storage[walked] = substitution } - /// Subtitutes each type variable occuring in `type` by its corresponding substitution in `self`, + /// Substitutes each type variable occurring in `type` by its corresponding substitution in `self`, /// apply `substitutionPolicy` to deal with free variables. /// /// The default substitution policy is `substituteByError` because we typically use `reify` after @@ -102,7 +102,7 @@ struct SubstitutionMap { } } - /// Returns `r` where each type variable occuring in its generic arguments of `r` are replaced by + /// Returns `r` where each type variable occurring in its generic arguments of `r` are replaced by /// their corresponding value in `self`, applying `substitutionPolicy` to handle free variables. func reify( _ r: DeclReference, withVariables substitutionPolicy: SubstitutionPolicy diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 55e80a09b..db47fe431 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -205,7 +205,7 @@ struct TypeChecker { } default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -274,7 +274,7 @@ struct TypeChecker { func transform(mutating me: inout Self, _ t: BoundGenericType) -> AnyType { ^t.transformArguments(mutating: &me) { (me, v) in - let w = (v as? AnyType) ?? fatalError("not implemented") + let w = (v as? AnyType) ?? UNIMPLEMENTED() return w.transform(mutating: &me, transform) } } @@ -326,7 +326,7 @@ struct TypeChecker { return EqualityConstraint(a, b, origin: origin) case .instance, .predicate: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -398,7 +398,7 @@ struct TypeChecker { /// if type checking failed. /// /// Type checking typically consists of visiting a declaration to generate proof obligations that - /// are then discharged by solving type constrants and/or type checking additional declarations. + /// are then discharged by solving type constraints and/or type checking additional declarations. /// /// - Parameters: /// - d: The declaration to type check. @@ -989,7 +989,7 @@ struct TypeChecker { } /// Returns a concrete or syntheszed implementation of requirement `r` in `concept` for `model` - /// exposed ot `scopeOfUse`, or `nil` if no such implementation exist. + /// exposed to `scopeOfUse`, or `nil` if no such implementation exist. func implementation(of r: AnyDeclID) { // FIXME: remove me if r.kind == AssociatedTypeDecl.self { return } @@ -1037,10 +1037,10 @@ struct TypeChecker { ) -> AnyDeclID? { switch requirement.kind { case AssociatedTypeDecl.self: - fatalError("not implemented") + UNIMPLEMENTED() case AssociatedValueDecl.self: - fatalError("not implemented") + UNIMPLEMENTED() case FunctionDecl.self: return implementation( @@ -1058,7 +1058,7 @@ struct TypeChecker { collectingCandidatesWith: collectFunction) case SubscriptImpl.self: - fatalError("not implemented") + UNIMPLEMENTED() default: unexpected(requirement, in: program.ast) @@ -1480,7 +1480,7 @@ struct TypeChecker { /// Computes and returns the type of `d`. private mutating func _uncheckedType(of d: T.ID) -> AnyType { let (i, cs) = eval(existentialBound: program[d].subject) - assert(cs.isEmpty, "not implemented") + guard cs.isEmpty else { UNIMPLEMENTED() } switch i.base { case is BuiltinType, is RemoteType: @@ -1508,7 +1508,7 @@ struct TypeChecker { assert(program[d].receiver == nil) let captures = uncheckedCaptureTypes(of: d) - let e = TupleType(captures.explict + captures.implicit) + let e = TupleType(captures.explicit + captures.implicit) return ^LambdaType(environment: ^e, inputs: inputs, output: output) } @@ -1654,7 +1654,7 @@ struct TypeChecker { e = TupleType([.init(label: "self", type: ^RemoteType(.yielded, r))]) } else { let captures = uncheckedCaptureTypes(of: d) - e = TupleType(captures.explict + captures.implicit) + e = TupleType(captures.explicit + captures.implicit) } return ^SubscriptType( @@ -1721,7 +1721,7 @@ struct TypeChecker { /// - Requires: `d` is not a member declaration. private mutating func uncheckedCaptureTypes( of d: T.ID - ) -> (explict: [TupleType.Element], implicit: [TupleType.Element]) { + ) -> (explicit: [TupleType.Element], implicit: [TupleType.Element]) { let e = explicitCaptures(program[d].explicitCaptures, of: d) let i = implicitCaptures(of: d, ignoring: Set(e.map(\.label!))) return (e, i) @@ -1893,7 +1893,7 @@ struct TypeChecker { /// Returns the implicit captures found in the body of `d`. private mutating func implicitCaptures( - of d: T.ID, ignoring explictCaptures: Set + of d: T.ID, ignoring explicitCaptures: Set ) -> [TupleType.Element] { // Only local declarations have captures. if !program.isLocal(d) { @@ -1904,8 +1904,8 @@ struct TypeChecker { var captureToStemAndEffect: [AnyDeclID: (stem: String, effect: AccessEffect)] = [:] for (name, mutability) in program.ast.uses(in: AnyDeclID(d)) { guard - let (stem, pick) = lookupImplicitCapture(name, occuringIn: d), - !explictCaptures.contains(stem) + let (stem, pick) = lookupImplicitCapture(name, occurringIn: d), + !explicitCaptures.contains(stem) else { continue } modify(&captureToStemAndEffect[pick, default: (stem, .let)]) { (x) in @@ -2011,8 +2011,8 @@ struct TypeChecker { private mutating func evalTypeAnnotation(_ e: ExistentialTypeExpr.ID) -> AnyType { let (i, cs) = eval(existentialInterface: program[e].traits) - assert(cs.isEmpty, "not implemented") - assert(program[e].whereClause == nil, "not implemented") + guard cs.isEmpty else { UNIMPLEMENTED() } + guard program[e].whereClause == nil else { UNIMPLEMENTED() } return ^ExistentialType(i, constraints: cs) } @@ -2074,7 +2074,7 @@ struct TypeChecker { unreachable() default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -2118,7 +2118,7 @@ struct TypeChecker { } } - /// Evalutes and returns the parameter annotations of `e`.ns. + /// Evaluates and returns the parameter annotations of `e`.ns. private mutating func evalParameterAnnotations( of e: LambdaTypeExpr.ID ) -> [CallableTypeParameter] { @@ -2134,7 +2134,7 @@ struct TypeChecker { /// /// The value of `d`'s explicit return type annotation is returned if it is defined. Otherwise, /// a fresh type variable is returned if `d` appears in an expression context (i.e., `d` is the - /// underlyng declaration of a lambda). Otherwise, `.void` is returned. + /// underlying declaration of a lambda). Otherwise, `.void` is returned. private mutating func evalReturnTypeAnnotation(of d: FunctionDecl.ID) -> AnyType { if let o = program[d].output { return evalTypeAnnotation(o) @@ -2199,7 +2199,7 @@ struct TypeChecker { // where that parameter is introduced. if let b = BoundGenericType(i) { for (p, a) in b.arguments { - assert(GenericTypeParameterType(a as! AnyType)?.decl == p, "not implemented") + if GenericTypeParameterType(a as! AnyType)?.decl != p { UNIMPLEMENTED() } } return (b.base, []) } @@ -2530,11 +2530,11 @@ struct TypeChecker { /// Returns the stem and declaration of `c`, which occurs in `d`, or `nil` if either `c` doesn't /// have to be captured or lookup failed. private mutating func lookupImplicitCapture( - _ c: NameExpr.ID, occuringIn d: T.ID + _ c: NameExpr.ID, occurringIn d: T.ID ) -> (stem: String, decl: AnyDeclID)? { let n = program[c].name var candidates = lookup(unqualified: n.value.stem, in: program[c].scope) - candidates.removeAll(where: { isCaptured(referenceTo: $0, occuringIn: AnyScopeID(d)) }) + candidates.removeAll(where: { isCaptured(referenceTo: $0, occurringIn: AnyScopeID(d)) }) if candidates.isEmpty { return nil } guard let pick = candidates.uniqueElement else { @@ -2977,7 +2977,7 @@ struct TypeChecker { } /// Returns the declarations of `name` interpreted as a compiler-known type alias (e.g., `Never` - /// or `Union`) specialized by `arguments`, or `nil` if an error occured. + /// or `Union`) specialized by `arguments`, or `nil` if an error occurred. private mutating func resolve( compilerKnownAlias name: SourceRepresentable, specializedBy arguments: [any CompileTimeValue], @@ -3047,7 +3047,7 @@ struct TypeChecker { metatype name: SourceRepresentable, specializedBy arguments: [any CompileTimeValue] ) -> NameResolutionResult.CandidateSet { if let a = arguments.uniqueElement { - let instance = (a as? AnyType) ?? fatalError("not implemented") + let instance = (a as? AnyType) ?? UNIMPLEMENTED() return [.compilerKnown(^MetatypeType(of: MetatypeType(of: instance)))] } @@ -3175,7 +3175,7 @@ struct TypeChecker { /// Returns `true` if references to `d` are captured if they occur in `scopeOfUse`. private mutating func isCaptured( - referenceTo d: AnyDeclID, occuringIn scopeOfUse: AnyScopeID + referenceTo d: AnyDeclID, occurringIn scopeOfUse: AnyScopeID ) -> Bool { if program.isContained(program[d].scope, in: scopeOfUse) { return false } if program.isGlobal(d) { return false } @@ -3316,7 +3316,7 @@ struct TypeChecker { var cs = candidate.constraints.union(t.constraints) let r = candidate.reference.modifyingArguments(mutating: &substitutions) { (s, v) in - let v = (v as? AnyType) ?? fatalError("not implemented") + let v = (v as? AnyType) ?? UNIMPLEMENTED() let x = instantiate(v, in: ctx, cause: cause, updating: &s) cs.formUnion(x.constraints) return x.shape @@ -3348,7 +3348,7 @@ struct TypeChecker { func instantiate(mutating me: inout Self, type: AnyType) -> TypeTransformAction { switch type.base { case is AssociatedTypeType: - fatalError("not implemented") + UNIMPLEMENTED() case let p as GenericTypeParameterType: if let t = substitutions[p.decl] { @@ -3688,7 +3688,7 @@ struct TypeChecker { let inputs = uncheckedInputTypes(of: e, withHint: h) let captures = uncheckedCaptureTypes(of: d) - let environment = TupleType(captures.explict + captures.implicit) + let environment = TupleType(captures.explicit + captures.implicit) let output = inferredReturnType(of: e, withHint: h?.output, updating: &obligations) if output.isError { @@ -3892,7 +3892,7 @@ struct TypeChecker { // The callee has a metatype and is a name expression bound to a nominal type declaration, // meaning that the call is actually a sugared buffer type expression. if isBoundToNominalTypeDecl(program[e].callee, in: obligations) { - fatalError("not implemented") + UNIMPLEMENTED() } // The callee has a callable type or we need inference to determine its type. Either way, @@ -4377,7 +4377,7 @@ struct TypeChecker { /// `scopeOfUse`, instantiating generic type constraints at `site`. /// /// `t1` is more specific than `t2` if both are callable types with the same labels and `t1` - /// accepts stricly less arguments than `t2`. + /// accepts strictly less arguments than `t2`. private mutating func compareSpecificity( _ lhs: AnyType, _ rhs: AnyType, in scopeOfUse: AnyScopeID, at site: SourceRange ) -> StrictPartialOrdering { @@ -4467,7 +4467,7 @@ struct TypeChecker { default: // TODO: should probably emit a diagnostic. Operator declarations cannot be overloaded. - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -4478,7 +4478,7 @@ struct TypeChecker { /// Creates a fresh type variable. /// - /// The returned instance is unique accress concurrent type checker instances. + /// The returned instance is unique access concurrent type checker instances. mutating func freshVariable() -> TypeVariable { defer { nextFreshVariableIdentifier += 1 } return .init(nextFreshVariableIdentifier) diff --git a/Sources/FrontEnd/TypedProgram.swift b/Sources/FrontEnd/TypedProgram.swift index ffc4d9b7e..bcd826800 100644 --- a/Sources/FrontEnd/TypedProgram.swift +++ b/Sources/FrontEnd/TypedProgram.swift @@ -174,7 +174,7 @@ public struct TypedProgram { if let t = v as? AnyType { return checker.specialize(t, for: specialization, in: scopeOfUse) } else { - fatalError("not implemented") + UNIMPLEMENTED() } } } @@ -415,7 +415,7 @@ public struct TypedProgram { return checker.scopeExtended(by: d) } - /// Returns the modules visibles to `s`: + /// Returns the modules visible to `s`: private func modules(exposedTo s: AnyScopeID) -> Set { if let m = ModuleDecl.ID(s) { return [m] diff --git a/Sources/GenerateHyloFileTests/FullPathInFatalErrors.swift b/Sources/GenerateHyloFileTests/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/GenerateHyloFileTests/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/IR/AbstractTypeLayout.swift b/Sources/IR/AbstractTypeLayout.swift index e3e2f2b51..3b52a2450 100644 --- a/Sources/IR/AbstractTypeLayout.swift +++ b/Sources/IR/AbstractTypeLayout.swift @@ -40,7 +40,7 @@ public struct AbstractTypeLayout { offset(of: n).map({ self[$0] }) } - /// Returns the offset of the stored poperty named `n` or `nil` if no such property exists. + /// Returns the offset of the stored property named `n` or `nil` if no such property exists. public func offset(of n: String) -> Int? { properties.firstIndex(where: { $0.label == n }) } diff --git a/Sources/IR/Analysis/AbstractInterpreter.swift b/Sources/IR/Analysis/AbstractInterpreter.swift index 8be72ba77..baaafaa31 100644 --- a/Sources/IR/Analysis/AbstractInterpreter.swift +++ b/Sources/IR/Analysis/AbstractInterpreter.swift @@ -8,7 +8,7 @@ struct AbstractInterpreter { /// The knowledge of the abstract interpreter about a single block. typealias BlockState = (sources: Set, before: Context, after: Context) - /// A map fron function block to the context of the abstract interpreter before and after the + /// A map from function block to the context of the abstract interpreter before and after the /// evaluation of its instructions. typealias State = [Function.Blocks.Address: BlockState] diff --git a/Sources/IR/Analysis/ControlFlowGraph.swift b/Sources/IR/Analysis/ControlFlowGraph.swift index 2797cafcf..71e5539f0 100644 --- a/Sources/IR/Analysis/ControlFlowGraph.swift +++ b/Sources/IR/Analysis/ControlFlowGraph.swift @@ -24,7 +24,7 @@ struct ControlFlowGraph { } - /// The way a control-flow relation is represeted internally. + /// The way a control-flow relation is represented internally. private typealias Relation = DirectedGraph /// The relation encoded by the graph. diff --git a/Sources/IR/Analysis/DominatorTree.swift b/Sources/IR/Analysis/DominatorTree.swift index 52ba14b5e..2bc1bcf1c 100644 --- a/Sources/IR/Analysis/DominatorTree.swift +++ b/Sources/IR/Analysis/DominatorTree.swift @@ -6,8 +6,8 @@ import Utils /// - A block `b1` in a control-flow graph *dominates* a block `b2` if every path from the entry to /// `b2` must go through `b1`. By definition, every node dominates itself. /// - A block `b1` *strictly dominates* a block `b2` if `b1` dominates `b2` and `b1 != b2`. -/// - A block `b1` is the *immediately dominates* a block `b2` if `b1` stricly dominates `b2` and -/// there is no block `b3` that stricly dominates `b2`. +/// - A block `b1` is the *immediately dominates* a block `b2` if `b1` strictly dominates `b2` and +/// there is no block `b3` that strictly dominates `b2`. /// /// A dominator tree encodes the dominance relation of a control graph as a tree where a node is /// a basic blocks and its children are those it immediately dominates. diff --git a/Sources/IR/Analysis/Lifetime.swift b/Sources/IR/Analysis/Lifetime.swift index ae12501a3..6844c92c8 100644 --- a/Sources/IR/Analysis/Lifetime.swift +++ b/Sources/IR/Analysis/Lifetime.swift @@ -4,7 +4,7 @@ import Utils /// /// A lifetime rooted at a definition `d` is a region starting immediately after `d` and covering /// a set of uses dominated by `d`. The "upper boundaries" of a lifetime are the program points -/// immediately after the uses seqenced last in that region. +/// immediately after the uses sequenced last in that region. /// /// - Note: The definition of an operand `o` isn't part of `o`'s lifetime. struct Lifetime { @@ -94,7 +94,7 @@ extension Module { // al.'s "Computing Liveness Sets for SSA-Form Programs". // Find all blocks in which the operand is being used. - var occurences = uses[operand, default: []].reduce( + var occurrences = uses[operand, default: []].reduce( into: Set(), { (blocks, use) in blocks.insert(use.user.block) }) @@ -102,19 +102,19 @@ extension Module { let cfg = functions[site.function]!.cfg() var approximateCoverage: [Function.Blocks.Address: (isLiveIn: Bool, isLiveOut: Bool)] = [:] while true { - guard let occurence = occurences.popFirst() else { break } + guard let occurrence = occurrences.popFirst() else { break } - // `occurence` is the defining block. - if site.address == occurence { continue } + // `occurrence` is the defining block. + if site.address == occurrence { continue } // We already propagated liveness to the block's live-in set. - if approximateCoverage[occurence]?.isLiveIn ?? false { continue } + if approximateCoverage[occurrence]?.isLiveIn ?? false { continue } // Mark that the definition is live at the block's entry and propagate to its predecessors. - approximateCoverage[occurence, default: (false, false)].isLiveIn = true - for predecessor in cfg.predecessors(of: occurence) { + approximateCoverage[occurrence, default: (false, false)].isLiveIn = true + for predecessor in cfg.predecessors(of: occurrence) { approximateCoverage[predecessor, default: (false, false)].isLiveOut = true - occurences.insert(predecessor) + occurrences.insert(predecessor) } } diff --git a/Sources/IR/Analysis/Module+CloseBorrows.swift b/Sources/IR/Analysis/Module+CloseBorrows.swift index fc7f3c41d..1dee6c29a 100644 --- a/Sources/IR/Analysis/Module+CloseBorrows.swift +++ b/Sources/IR/Analysis/Module+CloseBorrows.swift @@ -110,7 +110,7 @@ extension Module { case let s as CaptureIn where use.index == 0: let p = provenances(s.target).uniqueElement! - assert(self[p] is AllocStack, "not implemented") + guard self[p] is AllocStack else { UNIMPLEMENTED() } let u = self.uses[p, default: []].first(where: { self[$0.user] is ReleaseCaptures })! return extend(lifetime: r, toInclude: u) diff --git a/Sources/IR/Analysis/Module+Depolymorphize.swift b/Sources/IR/Analysis/Module+Depolymorphize.swift index 3e0d7f727..3dbdd7d8f 100644 --- a/Sources/IR/Analysis/Module+Depolymorphize.swift +++ b/Sources/IR/Analysis/Module+Depolymorphize.swift @@ -135,7 +135,7 @@ extension Module { let sourceModule = ir.modules[ir.module(defining: f)]! var rewrittenBlocks: [Block.ID: Block.ID] = [:] - var rewrittenIntructions: [InstructionID: InstructionID] = [:] + var rewrittenInstructions: [InstructionID: InstructionID] = [:] for b in sourceModule[f].blocks.addresses { let source = Block.ID(f, b) @@ -225,9 +225,9 @@ extension Module { case is Yield: rewrite(yield: i, to: b) default: - fatalError("not implemented") + UNIMPLEMENTED() } - rewrittenIntructions[i] = InstructionID(b, self[b].instructions.lastAddress!) + rewrittenInstructions[i] = InstructionID(b, self[b].instructions.lastAddress!) } /// Rewrites `i`, which is in `r.function`, into `result`, at the end of `b`. @@ -498,7 +498,7 @@ extension Module { case .parameter(let b, let i): return .parameter(rewrittenBlocks[b]!, i) case .register(let s): - return .register(rewrittenIntructions[s]!) + return .register(rewrittenInstructions[s]!) } } } diff --git a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift index b520e54f6..3dd0e15b6 100644 --- a/Sources/IR/Analysis/Module+NormalizeObjectStates.swift +++ b/Sources/IR/Analysis/Module+NormalizeObjectStates.swift @@ -177,7 +177,7 @@ extension Module { case .inout: assert(self[f.instruction!].isAccess(callee.receiverEffect)) default: - fatalError("not implemented") + UNIMPLEMENTED() } // Evaluate the arguments. @@ -447,7 +447,7 @@ extension Module { let locations: [AbstractLocation] if case .constant = addr.recordAddress { // Operand is a constant. - fatalError("not implemented") + UNIMPLEMENTED() } else { locations = context.locals[addr.recordAddress]!.unwrapLocations()!.map({ @@ -470,7 +470,7 @@ extension Module { let s = self[i] as! WrapExistentialAddr if case .constant = s.witness { // Operand is a constant. - fatalError("not implemented") + UNIMPLEMENTED() } context.locals[.register(i)] = context.locals[s.witness] @@ -562,7 +562,7 @@ extension Module { exitedWith exit: InstructionID, in context: inout Context ) { - // Skip the instruction if an error occured upstream. + // Skip the instruction if an error occurred upstream. guard let v = context.locals[start] else { assert(diagnostics.containsError) return @@ -716,13 +716,13 @@ extension Context { case .full(.uninitialized), .full(.consumed): return false default: - fatalError("not implemented") + UNIMPLEMENTED() } } } -/// A map fron function block to the context of the abstract interpreter before and after the +/// A map from function block to the context of the abstract interpreter before and after the /// evaluation of its instructions. private typealias Contexts = [Function.Blocks.Address: (before: Context, after: Context)] diff --git a/Sources/IR/Analysis/Module+Ownership.swift b/Sources/IR/Analysis/Module+Ownership.swift index f877e2bd5..d20192500 100644 --- a/Sources/IR/Analysis/Module+Ownership.swift +++ b/Sources/IR/Analysis/Module+Ownership.swift @@ -63,7 +63,7 @@ extension Module { // Access is expected to be reified at this stage. let request = s.capabilities.uniqueElement! - // Skip the instruction if an error occured upstream. + // Skip the instruction if an error occurred upstream. guard context.locals[s.source] != nil else { assert(diagnostics.containsError) return @@ -108,7 +108,7 @@ extension Module { } } - // Don't set the locals if an error occured to avoid cascading errors downstream. + // Don't set the locals if an error occurred to avoid cascading errors downstream. if !hasConflict { context.locals[.register(i)] = context.locals[s.source]! } @@ -157,7 +157,7 @@ extension Module { func interpret(endBorrow i: InstructionID, in context: inout Context) { let end = self[i] as! EndAccess - // Skip the instruction if an error occured upstream. + // Skip the instruction if an error occurred upstream. guard context.locals[end.start] != nil else { assert(diagnostics.containsError) return @@ -262,10 +262,10 @@ extension Module { let s = self[i] as! SubfieldView if case .constant = s.recordAddress { // Operand is a constant. - fatalError("not implemented") + UNIMPLEMENTED() } - // Skip the instruction if an error occured upstream. + // Skip the instruction if an error occurred upstream. guard let base = context.locals[s.recordAddress] else { assert(diagnostics.containsError) return @@ -280,7 +280,7 @@ extension Module { let s = self[i] as! WrapExistentialAddr if case .constant = s.witness { // Operand is a constant. - fatalError("not implemented") + UNIMPLEMENTED() } context.locals[.register(i)] = context.locals[s.witness] @@ -292,7 +292,7 @@ extension Module { region start: Operand, projecting access: AccessEffect, exitedWith exit: InstructionID, in context: inout Context ) { - // Skip the instruction if an error occured upstream. + // Skip the instruction if an error occurred upstream. guard let v = context.locals[start] else { assert(diagnostics.containsError) return diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index a3fedc322..d8ed974de 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -244,7 +244,7 @@ struct Emitter { return ast[s].site default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -341,7 +341,7 @@ struct Emitter { let explicit = program[bundle].explicitCaptures let implicit = program[bundle].implicitCaptures - // Exlicit captures appear first. + // Explicit captures appear first. for (i, c) in explicit.enumerated() { locals[c] = .parameter(entry, i) } @@ -388,7 +388,7 @@ struct Emitter { case .return(let s): insert(module.makeReturn(at: ast[s].site)) default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -430,7 +430,7 @@ struct Emitter { /// /// - Requires: `d` is a global binding. private mutating func lower(globalBinding d: BindingDecl.ID) { - fatalError("not implemented") + UNIMPLEMENTED() } /// Inserts the IR for the local binding `d`. @@ -515,7 +515,7 @@ struct Emitter { emitStore(value: initializer, to: x0) insert(module.makeCloseUnion(x0, at: ast[name].site)) } else { - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -585,7 +585,7 @@ struct Emitter { case .moveAssignment: return withClearContext({ $0.lower(syntheticMoveAssign: d) }) case .copy: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -871,7 +871,7 @@ struct Emitter { case .return(let s): emitControlFlow(return: s) default: - fatalError("not implemented") + UNIMPLEMENTED() } insertionPoint = .end(of: secondBranch) @@ -887,7 +887,7 @@ struct Emitter { case .return(let s): emitControlFlow(return: s) default: - fatalError("not implemented") + UNIMPLEMENTED() } insertionPoint = .end(of: tail) @@ -934,7 +934,7 @@ struct Emitter { if case .return = a { return a } else { - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -953,7 +953,7 @@ struct Emitter { emitDeinit(v, at: ast[s].site) if !module.type(of: v).ast.isVoidOrNever { // TODO: complain about unused value - fatalError("not implemented") + UNIMPLEMENTED() } return .next } @@ -986,7 +986,7 @@ struct Emitter { case .return(let s): emitControlFlow(return: s) default: - fatalError("not implemented") + UNIMPLEMENTED() } // Exit. @@ -1077,7 +1077,7 @@ struct Emitter { case .down: emitStore(downcast: e, to: storage) case .pointerConversion: - unreachable("pointer to address conversion evalutes to a lvalue") + unreachable("pointer to address conversion evaluates to a lvalue") } } @@ -1093,7 +1093,7 @@ struct Emitter { } // Otherwise, wrap the LHS. - fatalError("not implemented") + UNIMPLEMENTED() } /// Inserts the IR for storing the value of `e` to `storage`. @@ -1112,7 +1112,7 @@ struct Emitter { // TODO _ = lhs - fatalError("not implementeds") + UNIMPLEMENTED() } /// Inserts the IR for storing the value of `e` to `storage`. @@ -1195,7 +1195,7 @@ struct Emitter { var i = 1 for b in program[e].decl.explicitCaptures { // TODO: See #878 - precondition(program[b].pattern.subpattern.kind == NamePattern.self, "not implemented") + guard program[b].pattern.subpattern.kind == NamePattern.self else { UNIMPLEMENTED() } let y0 = insert(module.makeSubfieldView(of: storage, subfield: [i], at: site))! emitStore(value: program[b].initializer!, to: y0) i += 1 @@ -1317,7 +1317,7 @@ struct Emitter { case ast.coreType("Float32")!: emitStore(floatingPoint: literal, to: storage, evaluatedBy: FloatingPointConstant.float32(_:)) default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -1463,7 +1463,7 @@ struct Emitter { // TODO: Handle remote types let p = ParameterType(callee.inputs[i].type)! if p.bareType.base is RemoteType { - fatalError("not implemented") + UNIMPLEMENTED() } let s = emitSubfieldView(receiver, at: [i], at: ast[call].site) @@ -1605,7 +1605,7 @@ struct Emitter { case .direct(let d, let a) where d.kind == FunctionDecl.self: // Callee is a direct reference to a function declaration. guard calleeType.environment == .void else { - fatalError("not implemented") + UNIMPLEMENTED() } let specialization = module.specialization(in: insertionFunction!).merging(a) { (x, y) in @@ -1647,7 +1647,7 @@ struct Emitter { case .yielded: unreachable() case .set, .sink: - fatalError("not implemented") + UNIMPLEMENTED() case let k: let l = emitLValue(callee) @@ -1669,7 +1669,7 @@ struct Emitter { return emit(subscriptCallee: ast[InoutExpr.ID(callee)!].subject) default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -1681,7 +1681,7 @@ struct Emitter { // Callee is a direct reference to a subscript declaration. let t = SubscriptType(canonical(program[d].type))! guard t.environment == .void else { - fatalError("not implemented") + UNIMPLEMENTED() } let b = SubscriptBundleReference(to: SubscriptDecl.ID(d)!, parameterizedBy: a) @@ -1702,7 +1702,7 @@ struct Emitter { unreachable() default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -1745,7 +1745,7 @@ struct Emitter { continue } - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -1761,7 +1761,7 @@ struct Emitter { else failure: Block.ID, in scope: AnyScopeID ) -> Block.ID { // TODO: Implement narrowing to an arbitrary subtype. - assert(containerType.elements.contains(patternType), "not implemented") + guard containerType.elements.contains(patternType) else { UNIMPLEMENTED() } let site = ast[pattern].site let i = program.discriminatorToElement(in: containerType).firstIndex(of: patternType)! @@ -1930,7 +1930,7 @@ struct Emitter { return x0 case .synthetic: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -1967,7 +1967,7 @@ struct Emitter { return x4 case .synthetic: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -2012,7 +2012,7 @@ struct Emitter { return insert(module.makePointerToAddress(x2, to: target, at: ast[e].site))! default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -2067,7 +2067,7 @@ struct Emitter { return emitProperty(boundTo: receiver, declaredBy: d, specializedBy: a, at: site) case .constructor: - fatalError("not implemented") + UNIMPLEMENTED() case .builtinModule, .builtinFunction, .builtinType, .compilerKnownType: // Built-in symbols and compiler-known types are never used as l-value. @@ -2100,7 +2100,7 @@ struct Emitter { // Handle global bindings. if d.kind == VarDecl.self { - fatalError("not implemented") + UNIMPLEMENTED() } // Handle references to type declarations. @@ -2111,7 +2111,7 @@ struct Emitter { } // Handle references to global functions. - fatalError("not implemented") + UNIMPLEMENTED() } /// Returns the address of the member declared by `d`, specialized with `specialization` and @@ -2132,12 +2132,12 @@ struct Emitter { return emitSubfieldView(receiver, at: [i], at: site) default: - fatalError("not implemented") + UNIMPLEMENTED() } } /// Returns the projection the property declared by `d`, specialized with `specialization` and - /// bound to `reciever`, inserting IR anchored at `site`. + /// bound to `receiver`, inserting IR anchored at `site`. private mutating func emitComputedProperty( boundTo receiver: Operand, declaredByBundle d: SubscriptDecl.ID, specializedBy specialization: GenericArguments, diff --git a/Sources/IR/FullPathInFatalErrors.swift b/Sources/IR/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/IR/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/IR/Mangling/Demangler.swift b/Sources/IR/Mangling/Demangler.swift index 01b2838bc..142967e2c 100644 --- a/Sources/IR/Mangling/Demangler.swift +++ b/Sources/IR/Mangling/Demangler.swift @@ -3,7 +3,7 @@ import Core /// Hylo's demangling algorithm. struct Demangler { - /// The list of demangled symbols, in order of appearence (a.k.a. the symbol lookup table). + /// The list of demangled symbols, in order of appearance (a.k.a. the symbol lookup table). private var symbols: [DemangledSymbol] = [] /// Creates an instance with an empty lookup table. @@ -133,7 +133,7 @@ struct Demangler { continue } - // Otherise, we're done. + // Otherwise, we're done. return d } @@ -191,7 +191,7 @@ struct Demangler { case .endOfSequence: break case .whereClause: - fatalError("not implemented") + UNIMPLEMENTED() default: return nil } diff --git a/Sources/IR/Mangling/Mangler.swift b/Sources/IR/Mangling/Mangler.swift index 5554de301..ed121f9cc 100644 --- a/Sources/IR/Mangling/Mangler.swift +++ b/Sources/IR/Mangling/Mangler.swift @@ -283,7 +283,7 @@ struct Mangler { write(string: program.ast[e].site.text, to: &output) default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -299,7 +299,7 @@ struct Mangler { case .synthesized(let d): write(synthesized: d, to: &output) case .existentialized: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -337,7 +337,7 @@ struct Mangler { } default: - fatalError("not implemented") + UNIMPLEMENTED() } } @@ -353,7 +353,7 @@ struct Mangler { if let t = symbol as? AnyType { mangle(type: t, to: &output) } else { - fatalError("not implemented") + UNIMPLEMENTED() } } diff --git a/Sources/IR/Module.swift b/Sources/IR/Module.swift index e71d3e115..f99bfe9f3 100644 --- a/Sources/IR/Module.swift +++ b/Sources/IR/Module.swift @@ -388,7 +388,7 @@ public struct Module { let d = program.ast.moveRequirement(k) switch c.implementations[d]! { case .concrete: - fatalError("not implemented") + UNIMPLEMENTED() case .synthetic(let s): return demandDeclaration(lowering: s) @@ -513,7 +513,7 @@ public struct Module { let u = GenericTypeParameterType(t.instance) else { // TODO: Handle value parameters - fatalError("not implemented") + UNIMPLEMENTED() } result[p] = ^u diff --git a/Sources/IR/Operands/Instruction/Instruction.swift b/Sources/IR/Operands/Instruction/Instruction.swift index 3d57f1b07..e3cca3777 100644 --- a/Sources/IR/Operands/Instruction/Instruction.swift +++ b/Sources/IR/Operands/Instruction/Instruction.swift @@ -15,7 +15,7 @@ public protocol Instruction: CustomStringConvertible { /// Replaces the operand at position `i` with `o`. /// - /// Do not call this method direcly. Use `Module.replaceUses(of:with:)` instead to ensure def-use + /// Do not call this method directly. Use `Module.replaceUses(of:with:)` instead to ensure def-use /// ensure def-use chains are kept updated. mutating func replaceOperand(at i: Int, with new: Operand) diff --git a/Sources/IR/Operands/Instruction/ProjectBundle.swift b/Sources/IR/Operands/Instruction/ProjectBundle.swift index 83792fb64..e7786e1f2 100644 --- a/Sources/IR/Operands/Instruction/ProjectBundle.swift +++ b/Sources/IR/Operands/Instruction/ProjectBundle.swift @@ -15,7 +15,7 @@ public struct ProjectBundle: Instruction { /// The arguments of the call. /// /// Operands to non-`sink` inputs must be the result of an `access` instruction requesting a - /// capability for each variant in `calle` and having no use before `project`. + /// capability for each variant in `callee` and having no use before `project`. public private(set) var operands: [Operand] /// The site of the code corresponding to that instruction. diff --git a/Sources/IR/Operands/Instruction/ReleaseCaptures.swift b/Sources/IR/Operands/Instruction/ReleaseCaptures.swift index 787d718b6..a6b3a1a02 100644 --- a/Sources/IR/Operands/Instruction/ReleaseCaptures.swift +++ b/Sources/IR/Operands/Instruction/ReleaseCaptures.swift @@ -27,7 +27,7 @@ public struct ReleaseCaptures: Instruction { extension Module { - /// Creates a `release_capture` anchored at `site` that relases the accesses previously captured + /// Creates a `release_capture` anchored at `site` that releases the accesses previously captured /// in `container`. func makeReleaseCapture(_ container: Operand, at site: SourceRange) -> ReleaseCaptures { precondition(container.instruction.map({ self[$0] is AllocStack }) ?? false) diff --git a/Sources/IR/Program.swift b/Sources/IR/Program.swift index d92ccd221..d85bdb2d0 100644 --- a/Sources/IR/Program.swift +++ b/Sources/IR/Program.swift @@ -45,7 +45,7 @@ public struct Program: Core.Program { case .loweredSubscript(let d): return base.module(containing: base[d].scope) case .monomorphized: - fatalError("not implemented") + UNIMPLEMENTED() case .existentialized(let i): return module(defining: i) case .synthesized(let d): diff --git a/Sources/TestUtils/AnnotatedHyloFileTest.swift b/Sources/TestUtils/AnnotatedHyloFileTest.swift index c6e4defc0..f5de81d70 100644 --- a/Sources/TestUtils/AnnotatedHyloFileTest.swift +++ b/Sources/TestUtils/AnnotatedHyloFileTest.swift @@ -1,4 +1,5 @@ import Core +import Driver import Utils import XCTest @@ -146,4 +147,77 @@ extension XCTestCase { return testFailures } + /// Compiles and runs the val file at `hyloFilePath`, `XCTAssert`ing that diagnostics and exit + /// codes match annotated expectations. + public func compileAndRun(_ hyloFilePath: String, expectSuccess: Bool) throws { + try checkAnnotatedHyloFileDiagnostics(inFileAt: hyloFilePath, expectSuccess: expectSuccess) { + (hyloSource, diagnostics) in + + var executable: URL + + do { + executable = try compile(hyloSource.url, with: ["--emit", "binary"]) + } catch let d as DiagnosticSet { + // Recapture the diagnostics so the annotation testing framework can use them. The need for + // this ugliness makes me wonder how important it is to test cli.execute, which after all is + // just a thin wrapper over cli.executeCommand (currently private). + diagnostics = d + throw d + } + + let (status, _) = try run(executable) + if status != 0 { + throw NonzeroExitCode(value: status) + } + } + } + + /// Compiles `input` with the given arguments and returns the URL of the output file, throwing + /// diagnostics if there are any errors. + public func compile(_ input: URL, with arguments: [String]) throws -> URL { + let output = FileManager.default.makeTemporaryFileURL() + let cli = try Driver.parse(arguments + ["-o", output.relativePath, input.relativePath]) + let (status, diagnostics) = try cli.execute() + if !status.isSuccess { + throw diagnostics + } + + XCTAssert( + !diagnostics.containsError, + "CLI reported success but \(input) contains errors: \(diagnostics.rendered())") + + #if os(Windows) + let executableSuffix = ".exe" + #else + let executableSuffix = "" + #endif + + XCTAssert( + FileManager.default.fileExists(atPath: output.relativePath + executableSuffix), + "Compilation output file not found: \(output.relativePath)") + + return output + } + + /// Runs `executable` and returns its exit status along with the text written to its standard + /// output. + public func run(_ executable: URL) throws -> (status: Int32, standardOutput: String) { + let pipe = Pipe() + let task = Process() + task.executableURL = executable + task.standardOutput = pipe + try task.run() + task.waitUntilExit() + + let standardOutput = String( + data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) + return (task.terminationStatus, standardOutput ?? "") + } + +} + +struct NonzeroExitCode: Error { + + var value: Int32 + } diff --git a/Sources/TestUtils/FullPathInFatalErrors.swift b/Sources/TestUtils/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/TestUtils/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/TestUtils/TestAnnotation.swift b/Sources/TestUtils/TestAnnotation.swift index dd161f8ed..f4ad3d95d 100644 --- a/Sources/TestUtils/TestAnnotation.swift +++ b/Sources/TestUtils/TestAnnotation.swift @@ -25,7 +25,7 @@ import XCTest /// /// An annotation body corresponds to a single annotation. When present, the line offset is parsed /// as a natural number prefixed by either `@+` or `@-`. The next sequence of non-whitespace -/// characters is parsed as the command. Preceeding whitespaces are ignored; following whitespaces +/// characters is parsed as the command. Preceding whitespaces are ignored; following whitespaces /// are ignored only if they are on the same line as the command. The remainder of the annotation /// body is parsed as the argument. /// diff --git a/Sources/Utils/DoublyLinkedList.swift b/Sources/Utils/DoublyLinkedList.swift index 9df2901df..5ca321331 100644 --- a/Sources/Utils/DoublyLinkedList.swift +++ b/Sources/Utils/DoublyLinkedList.swift @@ -44,7 +44,7 @@ public struct DoublyLinkedList { /// - Note: A bucket is said to be used if its element is not `nil`. fileprivate struct Bucket { - /// If the bucket is used, represents the offset of the preceeding bucket in the list, or `-1` + /// If the bucket is used, represents the offset of the preceding bucket in the list, or `-1` /// if such a bucket is not defined. Unspecified otherwise. var previousOffset: Int diff --git a/Sources/Utils/FullPathInFatalErrors.swift b/Sources/Utils/FullPathInFatalErrors.swift new file mode 100644 index 000000000..302fef325 --- /dev/null +++ b/Sources/Utils/FullPathInFatalErrors.swift @@ -0,0 +1,46 @@ +/// Just like Swift.precondition, but includes the full file path in the diagnostic. +func precondition( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.precondition(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.preconditionFailure, but includes the full file path in the diagnostic. +func preconditionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.preconditionFailure(message(), file: (file), line: line) +} + +/// Just like Swift.fatalError, but includes the full file path in the diagnostic. +func fatalError( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) -> Never { + Swift.fatalError(message(), file: (file), line: line) +} + +/// Just like Swift.assert, but includes the full file path in the diagnostic. +func assert( + _ condition: @autoclosure () -> Bool, + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assert(condition(), message(), file: (file), line: line) +} + +/// Just like Swift.assertionFailure, but includes the full file path in the diagnostic. +func assertionFailure( + _ message: @autoclosure () -> String = String(), + file: StaticString = #filePath, + line: UInt = #line +) { + Swift.assertionFailure(message(), file: (file), line: line) +} diff --git a/Sources/Utils/StatefulCoder.swift b/Sources/Utils/StatefulCoder.swift index 22a0def86..6a8e59cbb 100644 --- a/Sources/Utils/StatefulCoder.swift +++ b/Sources/Utils/StatefulCoder.swift @@ -4,7 +4,7 @@ import Foundation private let stateKey = CodingUserInfoKey(rawValue: UUID().uuidString)! /// An object that flattens values into—or reconstitutes values from—a serialized `Encoding`, -/// maintaing state across the (de)serialization of individual parts. +/// maintaining state across the (de)serialization of individual parts. /// /// - Note: this protocol matches Foundation's `XXXEncoder`/`XXXDecoder` types, /// which—confusingly—do not themselves conform to the `Encoder`/`Decoder` protocols. @@ -27,7 +27,7 @@ extension StatefulCoder { } -/// An object that flattens values into a serialized `Encoding`, maintaing state across the +/// An object that flattens values into a serialized `Encoding`, maintaining state across the /// serialization of individual parts. /// /// - Note: this protocol matches Foundation's `XXXEncoder` types, which—confusingly—do not @@ -52,7 +52,7 @@ extension StatefulEncoder { } -/// An object that reconstitutes values from a serialized `Encoding`, maintaing state across the +/// An object that reconstitutes values from a serialized `Encoding`, maintaining state across the /// deserialization of individual parts. /// /// - Note: this protocol matches Foundation's `XXXEncoder` types, which—confusingly—do not diff --git a/Sources/Utils/String+Extensions.swift b/Sources/Utils/String+Extensions.swift index 3c668a81b..17297c5ee 100644 --- a/Sources/Utils/String+Extensions.swift +++ b/Sources/Utils/String+Extensions.swift @@ -44,8 +44,8 @@ extension StringProtocol { return r } - /// Replace any sucession of whitespace characters (including newlines and tabs) with a single - /// white space charater. + /// Replace any succession of whitespace characters (including newlines and tabs) with a single + /// white space character. public func canonicalize() -> String { var remaining = self[.. String = "", - file: StaticString = #file, + file: StaticString = #filePath, line: UInt = #line ) -> Never { fatalError(message(), file: file, line: line) diff --git a/Sources/Utils/WideUInt.swift b/Sources/Utils/WideUInt.swift index 14c28d2a9..f5d4b250b 100644 --- a/Sources/Utils/WideUInt.swift +++ b/Sources/Utils/WideUInt.swift @@ -1,6 +1,6 @@ import BigInt -/// An unisgned integer with an arbitrary, but fixed, bit width. +/// An unsigned integer with an arbitrary, but fixed, bit width. public struct WideUInt { /// The underlying value. diff --git a/Tests/DriverTests/DriverTests.swift b/Tests/DriverTests/DriverTests.swift index 54ce6f03c..ea82f3a86 100644 --- a/Tests/DriverTests/DriverTests.swift +++ b/Tests/DriverTests/DriverTests.swift @@ -6,7 +6,7 @@ import XCTest final class DriverTests: XCTestCase { - /// The result of a compiler invokation. + /// The result of a compiler invocation. private struct CompilationResult { /// The exit status of the compiler. @@ -18,6 +18,13 @@ final class DriverTests: XCTestCase { /// The text of generated diagnostics. let diagnosticText: String + /// `XCTAssert`'s that `diagnosticText` matches `expectedDiagnostics`, with better output + /// formatting on failure. + func checkDiagnosticText( + `is` expectedDiagnostics: String, file: StaticString = #filePath, line: UInt = #line + ) { + XCTAssertEqual("\n" + diagnosticText, "\n" + expectedDiagnostics, file: file, line: line) + } } func testNoInput() throws { @@ -28,41 +35,41 @@ final class DriverTests: XCTestCase { let result = try compile(["--emit", "raw-ast"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) XCTAssert(FileManager.default.fileExists(atPath: result.output.relativePath)) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") } func testRawIR() throws { let result = try compile(["--emit", "raw-ir"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") XCTAssert(FileManager.default.fileExists(atPath: result.output.relativePath)) } func testIR() throws { let result = try compile(["--emit", "ir"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") XCTAssert(FileManager.default.fileExists(atPath: result.output.relativePath)) } func testLLVM() throws { let result = try compile(["--emit", "llvm"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") XCTAssert(FileManager.default.fileExists(atPath: result.output.relativePath)) } func testIntelASM() throws { let result = try compile(["--emit", "intel-asm"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") XCTAssert(FileManager.default.fileExists(atPath: result.output.relativePath)) } func testBinary() throws { let result = try compile(["--emit", "binary"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") #if os(Windows) XCTAssert(FileManager.default.fileExists(atPath: result.output.relativePath + ".exe")) @@ -75,20 +82,19 @@ final class DriverTests: XCTestCase { let valSource = try newFile(containing: "fun x") let result = try compile([], valSource) XCTAssertFalse(result.status.isSuccess) - XCTAssertEqual( - result.diagnosticText, - """ - \(valSource.relativePath):1.6: error: expected function signature - fun x - ^ - - """) + result.checkDiagnosticText( + is: """ + \(valSource.relativePath):1.6: error: expected function signature + fun x + ^ + + """) } func testTypeCheckSuccess() throws { let result = try compile(["--typecheck"], newFile(containing: "public fun main() {}")) XCTAssert(result.status.isSuccess) - XCTAssert(result.diagnosticText.isEmpty) + result.checkDiagnosticText(is: "") XCTAssertFalse(FileManager.default.fileExists(atPath: result.output.relativePath)) } @@ -96,14 +102,13 @@ final class DriverTests: XCTestCase { let valSource = try newFile(containing: "public fun main() { foo() }") let result = try compile(["--typecheck"], valSource) XCTAssertFalse(result.status.isSuccess) - XCTAssertEqual( - result.diagnosticText, - """ - \(valSource.relativePath):1.21-24: error: undefined name 'foo' in this scope - public fun main() { foo() } - ~~~ - - """) + result.checkDiagnosticText( + is: """ + \(valSource.relativePath):1.21-24: error: undefined name 'foo' in this scope + public fun main() { foo() } + ~~~ + + """) XCTAssertFalse(FileManager.default.fileExists(atPath: result.output.relativePath)) } diff --git a/Tests/EndToEndTests/ExecutionTests.swift b/Tests/EndToEndTests/ExecutionTests.swift index dd1f3088d..d7b39d63e 100644 --- a/Tests/EndToEndTests/ExecutionTests.swift +++ b/Tests/EndToEndTests/ExecutionTests.swift @@ -1,6 +1,3 @@ -import ArgumentParser -import Core -import Driver import TestUtils import XCTest @@ -19,78 +16,3 @@ final class ExecutionTests: XCTestCase { } } - -extension XCTestCase { - - /// Compiles and runs the val file at `hyloFilePath`, `XCTAssert`ing that diagnostics and exit - /// codes match annotated expectations. - func compileAndRun(_ hyloFilePath: String, expectSuccess: Bool) throws { - try checkAnnotatedHyloFileDiagnostics(inFileAt: hyloFilePath, expectSuccess: expectSuccess) { - (hyloSource, diagnostics) in - - var executable: URL - - do { - executable = try compile(hyloSource.url, with: ["--emit", "binary"]) - } catch let d as DiagnosticSet { - // Recapture the diagnostics so the annotation testing framework can use them. The need for - // this ugliness makes me wonder how important it is to test cli.execute, which after all is - // just a thin wrapper over cli.executeCommand (currently private). - diagnostics = d - throw d - } - - let (status, _) = try run(executable) - if status != 0 { - throw NonzeroExitCode(value: status) - } - } - } - - /// Compiles `input` with the given arguments and returns the URL of the output file, throwing - /// diagnostics if there are any errors. - fileprivate func compile(_ input: URL, with arguments: [String]) throws -> URL { - let output = FileManager.default.makeTemporaryFileURL() - let cli = try Driver.parse(arguments + ["-o", output.relativePath, input.relativePath]) - let (status, diagnostics) = try cli.execute() - if !status.isSuccess { - throw diagnostics - } - - XCTAssert( - !diagnostics.containsError, - "CLI reported success but \(input) contains errors: \(diagnostics.rendered())") - - #if os(Windows) - let executableSuffix = ".exe" - #else - let executableSuffix = "" - #endif - - XCTAssert( - FileManager.default.fileExists(atPath: output.relativePath + executableSuffix), - "Compilation output file not found: \(output.relativePath)") - - return output - } - - /// Runs `executable` and returns its exit status along with the text written to its standard - /// output. - fileprivate func run(_ executable: URL) throws -> (status: Int32, standardOutput: String) { - let pipe = Pipe() - let task = Process() - task.executableURL = executable - task.standardOutput = pipe - try task.run() - task.waitUntilExit() - - let standardOutput = String( - data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) - return (task.terminationStatus, standardOutput ?? "") - } - -} - -struct NonzeroExitCode: Error { - var value: Int32 -} diff --git a/Tests/EndToEndTests/TestCases/AddressOf.hylo b/Tests/EndToEndTests/TestCases/AddressOf.hylo index 18102aa85..2591143d5 100644 --- a/Tests/EndToEndTests/TestCases/AddressOf.hylo +++ b/Tests/EndToEndTests/TestCases/AddressOf.hylo @@ -2,6 +2,7 @@ public fun main() { let x = 42 - let y = Pointer.to[x] - y.with_pointee(fun(_ i) -> Void { precondition(i == 42) }) + let y = pointer[to: x] + let z = y.unsafe[] + precondition(z == 42) } diff --git a/Tests/EndToEndTests/TestCases/Pointer.hylo b/Tests/EndToEndTests/TestCases/Pointer.hylo index 622129a4a..6c2e9d9da 100644 --- a/Tests/EndToEndTests/TestCases/Pointer.hylo +++ b/Tests/EndToEndTests/TestCases/Pointer.hylo @@ -1,14 +1,9 @@ //- compileAndRun expecting: success public fun main() { - let p: MutableRawPointer = .allocate(count: 8, aligned_at: 8) - let q = MutablePointer(p) - q.with_uninitialized_pointee(fun (_ i: set Int) -> Void { - &i = 42 - }) - let x = q.with_mutable_pointee(fun (_ i: inout Int) -> Int { - i.copy() - }) + let q = PointerToMutable.allocate(count: 1) + q.unsafe_initialize_pointee(fun (_ i: set Int) -> Void { &i = 42 }) + let x = q.unsafe[].copy() q.deallocate() precondition(x == 42) diff --git a/Tests/HyloTests/BuiltinFunctionTests.swift b/Tests/HyloTests/BuiltinFunctionTests.swift index 04c3d158d..3e53a9d4f 100644 --- a/Tests/HyloTests/BuiltinFunctionTests.swift +++ b/Tests/HyloTests/BuiltinFunctionTests.swift @@ -189,7 +189,7 @@ final class BuiltinFunctionTests: XCTestCase { instructions: [String], parameterizedBy parameters: [[String]], createInstanceWithType expectedType: LambdaType, - file: StaticString = #file, + file: StaticString = #filePath, line: UInt = #line ) throws { var i: UInt64 = 0 diff --git a/Tests/HyloTests/LexerTests.swift b/Tests/HyloTests/LexerTests.swift index 9d4ec6d1a..07feb7a5a 100644 --- a/Tests/HyloTests/LexerTests.swift +++ b/Tests/HyloTests/LexerTests.swift @@ -357,7 +357,7 @@ final class LexerTests: XCTestCase { file: StaticString = #filePath, line: UInt = #line ) { - // The list of tokens should have the same lenght as that of the specifications. + // The list of tokens should have the same length as that of the specifications. XCTAssert( tokens.count == specs.count, "expected \(specs.count) token(s), found \(tokens.count)", diff --git a/Tests/HyloTests/ParserTests.swift b/Tests/HyloTests/ParserTests.swift index d7a3b3992..09f086a35 100644 --- a/Tests/HyloTests/ParserTests.swift +++ b/Tests/HyloTests/ParserTests.swift @@ -753,21 +753,21 @@ final class ParserTests: XCTestCase { XCTAssertNotNil(decl.defaultValue) } - func testParameterIntefaceLabelAndName() throws { + func testParameterInterfaceLabelAndName() throws { let input: SourceFile = "for name" let interface = try XCTUnwrap(try apply(Parser.parameterInterface, on: input).element) XCTAssertEqual(interface.label?.value, "for") XCTAssertEqual(interface.name.value, "name") } - func testParameterIntefaceUnderscoreAndName() throws { + func testParameterInterfaceUnderscoreAndName() throws { let input: SourceFile = "_ name" let interface = try XCTUnwrap(try apply(Parser.parameterInterface, on: input).element) XCTAssertNil(interface.label) XCTAssertEqual(interface.name.value, "name") } - func testParameterIntefaceOnlyName() throws { + func testParameterInterfaceOnlyName() throws { let input: SourceFile = "name" let interface = try XCTUnwrap(try apply(Parser.parameterInterface, on: input).element) XCTAssertEqual(interface.label?.value, "name") @@ -782,7 +782,7 @@ final class ParserTests: XCTestCase { XCTAssertNil(decl.precedenceGroup) } - func testOperatorDeclWithPredecenceGroup() throws { + func testOperatorDeclWithPrecedenceGroup() throws { let input: SourceFile = "operator infix+ : addition" let (declID, ast) = try input.parseWithDeclPrologue(with: Parser.parseOperatorDecl) let decl = try XCTUnwrap(ast[declID]) diff --git a/Tests/HyloTests/WideUIntExtensionsTests.swift b/Tests/HyloTests/WideUIntExtensionsTests.swift index 56d99a71e..9e40a7844 100644 --- a/Tests/HyloTests/WideUIntExtensionsTests.swift +++ b/Tests/HyloTests/WideUIntExtensionsTests.swift @@ -38,7 +38,7 @@ final class WideUIntExtensionsTests: XCTestCase { /// Asserts that `a` has given `description` and `bitWidth`. private func assert( _ a: WideUInt?, is description: String, withWidth bitWidth: Int, - file: StaticString = #file, line: UInt = #line + file: StaticString = #filePath, line: UInt = #line ) { guard let w = a else { XCTFail("expected '\(description)'", file: file, line: line) diff --git a/Tests/LibraryTests/LibraryTests.swift b/Tests/LibraryTests/LibraryTests.swift new file mode 100644 index 000000000..3b21aad6b --- /dev/null +++ b/Tests/LibraryTests/LibraryTests.swift @@ -0,0 +1 @@ +import TestUtils diff --git a/Tests/LibraryTests/TestCases/IntTests.hylo b/Tests/LibraryTests/TestCases/IntTests.hylo new file mode 100644 index 000000000..69aee7622 --- /dev/null +++ b/Tests/LibraryTests/TestCases/IntTests.hylo @@ -0,0 +1,6 @@ +//- compileAndRun expecting: success + +public fun main() { + // Int.init() + precondition(Int() == 0) +} diff --git a/Tests/ManglingTests/ManglingTests.swift b/Tests/ManglingTests/ManglingTests.swift index fa3ba49b5..93fe7954d 100644 --- a/Tests/ManglingTests/ManglingTests.swift +++ b/Tests/ManglingTests/ManglingTests.swift @@ -132,7 +132,7 @@ private struct SymbolCollector: ASTWalkObserver { private(set) var symbols: [String: AnyDeclID] = [:] /// Creates an instance observing the nodes in `p` and reporting assertion failures as though - /// they occured at line `l` of this source file.. + /// they occurred at line `l` of this source file.. init(forNodesIn p: TypedProgram, reportingFailuresAtLine l: UInt = #line) { self.program = p self.failuresReportingLine = l diff --git a/Tools/gyb.py b/Tools/gyb.py index 520d1dec3..2f55987cb 100755 --- a/Tools/gyb.py +++ b/Tools/gyb.py @@ -50,7 +50,7 @@ def split_lines(s): # Note: Where "# Absorb" appears below, the regexp attempts to eat up # through the end of ${...} and %{...}% constructs. In reality we -# handle this with the Python tokenizer, which avoids mis-detections +# handle this with the Python tokenizer, which avoids misdetections # due to nesting, comments and strings. This extra absorption in the # regexp facilitates testing the regexp on its own, by preventing the # interior of some of these constructs from being treated as literal diff --git a/Tools/retry-once b/Tools/retry-once new file mode 100755 index 000000000..705a7b7d2 --- /dev/null +++ b/Tools/retry-once @@ -0,0 +1,4 @@ +#!/usr/bin/env bash -e +# retry-once command... executes command... and if it fails, executes it again. +# because we seem to get spurious failures in various places with GitHub actions, +"$@" || "$@"