diff --git a/Sources/CodeGen/LLVM/Transpilation.swift b/Sources/CodeGen/LLVM/Transpilation.swift index 73fce8678..0649c62fc 100644 --- a/Sources/CodeGen/LLVM/Transpilation.swift +++ b/Sources/CodeGen/LLVM/Transpilation.swift @@ -1211,14 +1211,13 @@ extension SwiftyLLVM.Module { if let (_, b) = s.targets.elements.uniqueElement { insertBr(to: block[b]!, at: insertionPoint) } else { - let d = discriminator(s.scrutinee) - let t = UnionType(m.type(of: s.scrutinee).ast)! - let e = m.program.discriminatorToElement(in: t) + let e = m.program.discriminatorToElement(in: s.union) let branches = s.targets.map { (t, b) in (word().constant(e.firstIndex(of: t)!), block[b]!) } // The last branch is the "default". + let d = llvm(s.discriminator) insertSwitch( on: d, cases: branches.dropLast(), default: branches.last!.1, at: insertionPoint) diff --git a/Sources/IR/Analysis/Module+AccessReification.swift b/Sources/IR/Analysis/Module+AccessReification.swift index 10e9e1460..c788ff25b 100644 --- a/Sources/IR/Analysis/Module+AccessReification.swift +++ b/Sources/IR/Analysis/Module+AccessReification.swift @@ -64,7 +64,7 @@ extension Module { forEachClient(of: i) { (u) in let rs = requests(u) - lower = max(rs.weakest!, lower) + if let w = rs.weakest { lower = max(w, lower) } upper = rs.strongest(including: upper) } diff --git a/Sources/IR/Analysis/Module+CloseBorrows.swift b/Sources/IR/Analysis/Module+CloseBorrows.swift index 9ebc1b9a9..b95c03c0d 100644 --- a/Sources/IR/Analysis/Module+CloseBorrows.swift +++ b/Sources/IR/Analysis/Module+CloseBorrows.swift @@ -43,6 +43,12 @@ extension Module { this.makeCloseCapture(.register(i), at: site) } + case is OpenUnion: + let region = extendedLiveRange(of: .register(i)) + insertClose(i, atBoundariesOf: region) { (this, site) in + this.makeCloseUnion(.register(i), at: site) + } + case is Project: let region = extendedLiveRange(of: .register(i)) insertClose(i, atBoundariesOf: region) { (this, site) in diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index 41671d31e..6b87ba421 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -612,30 +612,27 @@ struct Emitter { /// /// - Requires: `d` is a local `let` or `inout` binding. private mutating func lower(projectedLocalBinding d: BindingDecl.ID) { - let access = AccessEffect(program[d].pattern.introducer.value) - precondition(access == .let || access == .inout) precondition(program.isLocal(d)) + let source = emitLValue(ast[d].initializer!) + assignProjections(of: source, to: program[d].pattern) + } - let initializer = ast[d].initializer! - let source = emitLValue(initializer) - let isSink = module.isSink(source) + /// Assigns the bindings declared in `d` to their corresponding projection of `rhs`. + private mutating func assignProjections(of rhs: Operand, to d: BindingPattern.ID) { + precondition(!program[d].introducer.value.isConsuming) + let k = AccessEffect(program[d].introducer.value) + let request: AccessEffectSet = module.isSink(rhs) ? [k, .sink] : [k] - for (path, name) in ast.names(in: program[d].pattern.subpattern) { - var part = emitSubfieldView(source, at: path, at: program[name].decl.site) + for (path, name) in ast.names(in: program[d].subpattern) { + var part = emitSubfieldView(rhs, at: path, at: program[name].decl.site) let partDecl = ast[name].decl - let t = canonical(program[partDecl].type) - part = emitCoerce(part, to: t, at: ast[partDecl].site) + let bindingType = canonical(program[partDecl].type) + part = emitCoerce(part, to: bindingType, at: ast[partDecl].site) - if isSink { - let b = module.makeAccess( - [.sink, access], from: part, correspondingTo: partDecl, at: ast[partDecl].site) - frames[partDecl] = insert(b)! - } else { - let b = module.makeAccess( - access, from: part, correspondingTo: partDecl, at: ast[partDecl].site) - frames[partDecl] = insert(b)! - } + let b = module.makeAccess( + request, from: part, correspondingTo: partDecl, at: ast[partDecl].site) + frames[partDecl] = insert(b)! } } @@ -740,7 +737,7 @@ struct Emitter { let targets = UnionSwitch.Targets( t.elements.map({ (e) in (key: e, value: appendBlock()) }), uniquingKeysWith: { (a, _) in a }) - insert(module.makeUnionSwitch(on: receiver, toOneOf: targets, at: site)) + emitUnionSwitch(on: receiver, toOneOf: targets, at: site) let tail = appendBlock() for (u, b) in targets { @@ -896,7 +893,7 @@ struct Emitter { let targets = UnionSwitch.Targets( t.elements.map({ (e) in (key: e, value: appendBlock()) }), uniquingKeysWith: { (a, _) in a }) - insert(module.makeUnionSwitch(on: source, toOneOf: targets, at: site)) + emitUnionSwitch(on: source, toOneOf: targets, at: site) let tail = appendBlock() for (u, b) in targets { @@ -1661,8 +1658,8 @@ struct Emitter { let calleeType = ArrowType(t)!.lifted // Emit the operands, starting with RHS. - let r = emit(infixOperand: rhs, passed: ParameterType(calleeType.inputs[1].type)!.access) - let l = emit(infixOperand: lhs, passed: ParameterType(calleeType.inputs[0].type)!.access) + let r = emit(infixOperand: rhs, passedTo: ParameterType(calleeType.inputs[1].type)!) + let l = emit(infixOperand: lhs, passedTo: ParameterType(calleeType.inputs[0].type)!) // The callee must be a reference to member function. guard case .member(let d, let a, _) = program[callee.expr].referredDecl else { @@ -2073,24 +2070,21 @@ struct Emitter { return (callee, captures + arguments) } - /// Inserts the IR for infix operand `e` passed with convention `access`. + /// Inserts the IR for infix operand `e` passed to a parameter of type `p`. private mutating func emit( - infixOperand e: FoldedSequenceExpr, passed access: AccessEffect + infixOperand e: FoldedSequenceExpr, passedTo p: ParameterType ) -> Operand { - let storage: Operand - switch e { - case .infix(let callee, _, _): - let t = ArrowType(canonical(program[callee.expr].type))!.lifted - storage = emitAllocStack(for: t.output, at: ast.site(of: e)) - emitStore(e, to: storage) + case .infix(let f, _, _): + let t = ArrowType(canonical(program[f.expr].type))!.lifted + let s = emitAllocStack(for: t.output, at: ast.site(of: e)) + emitStore(e, to: s) + let u = emitCoerce(s, to: p.bareType, at: ast.site(of: e)) + return insert(module.makeAccess(p.access, from: u, at: ast.site(of: e)))! case .leaf(let e): - let x0 = emitLValue(e) - storage = unwrapCapture(x0, at: program[e].site) + return emitArgument(e, to: p, at: program[e].site) } - - return insert(module.makeAccess(access, from: storage, at: ast.site(of: e)))! } /// Emits the IR of a call to `f` with given `arguments` at `site`. @@ -2308,37 +2302,37 @@ struct Emitter { return (entityToCall, [c]) } + /// Returns `(success: a, failure: b)` where `a` is the basic block reached if all items in /// `condition` hold and `b` is the basic block reached otherwise, creating new basic blocks /// in `scope`. private mutating func emitTest( condition: [ConditionItem], in scope: AnyScopeID ) -> (success: Block.ID, failure: Block.ID) { - let f = insertionFunction! - // Allocate storage for all the declarations in the condition before branching so that all - // `dealloc_stack` are to dominated by their corresponding `alloc_stack`. - var allocs: [Operand] = [] + // `dealloc_stack` are dominated by their corresponding `alloc_stack`. + var allocations: [Operand?] = [] for case .decl(let d) in condition { - let a = insert(module.makeAllocStack(program[d].type, at: ast[d].site))! - allocs.append(a) + if program[d].pattern.introducer.value.isConsuming { + allocations.append(insert(module.makeAllocStack(program[d].type, at: ast[d].site))) + } else { + allocations.append(nil) + } } - let failure = module.appendBlock(in: scope, to: f) + let failure = module.appendBlock(in: scope, to: insertionFunction!) for (i, item) in condition.enumerated() { switch item { case .expr(let e): let test = pushing(Frame(), { $0.emit(branchCondition: e) }) - let next = module.appendBlock(in: scope, to: f) + let next = appendBlock(in: scope) insert(module.makeCondBranch(if: test, then: next, else: failure, at: ast[e].site)) insertionPoint = .end(of: next) case .decl(let d): - let subject = emitLValue(ast[d].initializer!) - let patternType = canonical(program[d].type) let next = emitConditionalNarrowing( - subject, as: ast[d].pattern, typed: patternType, to: allocs[i], - else: failure, in: scope) + d, movingConsumedValuesTo: allocations[i], + branchingOnFailureTo: failure, in: scope) insertionPoint = .end(of: next) } } @@ -2346,68 +2340,65 @@ struct Emitter { return (success: insertionBlock!, failure: failure) } - /// Returns a basic block in which the names in `pattern` have been declared and initialized. + /// Returns a basic block in which the names in `d` have been declared and initialized. /// /// This method emits IR to: - /// - /// - check whether the value in `subject` is an instance of `patternType`; + /// - evaluate the `d`'s initializer as value *v*, + /// - check whether the value in *v* is an instance of `d`'s type; /// - if it isn't, jump to `failure`; - /// - if it is, jump to a new basic block *b*, coerce the contents of `subject` into `storage`, - /// applying consuming coercions as necessary, and define the bindings declared in `pattern`. + /// - if it is, jump to a new basic block and define and initialize the bindings declared in `d`. /// - /// If `subject` always matches `patternType`, the narrowing is irrefutable and `failure` is - /// unreachable in the generated IR. + /// If `d` has a consuming introducer (e.g., `var`), the value of `d`'s initializer is moved to + /// `storage`, which denotes a memory location with `d`'s type. Otherwise, `storage` is `nil` and + /// the bindings in `d` are defined as new projections. In either case, the emitter's context is + /// is updated to associate each binding to its value. /// - /// The return value is the new basic block *b*, which is defined in `scope`. The emitter context - /// is updated to associate the bindings declared in `pattern` to their address in `storage`. + /// The return value of the method is a basic block, defined in `scope`. If *v* has the same type + /// as `d`, the narrowing is irrefutable and `failure` is unreachable in the generated IR. private mutating func emitConditionalNarrowing( - _ subject: Operand, - as pattern: BindingPattern.ID, typed patternType: AnyType, - to storage: Operand, - else failure: Block.ID, in scope: AnyScopeID + _ d: BindingDecl.ID, + movingConsumedValuesTo storage: Operand?, + branchingOnFailureTo failure: Block.ID, + in scope: AnyScopeID ) -> Block.ID { - switch module.type(of: subject).ast.base { - case let t as UnionType: - return emitConditionalNarrowing( - subject, typed: t, as: pattern, typed: patternType, to: storage, - else: failure, in: scope) - default: - break - } + let lhsType = canonical(program[d].type) + let rhs = emitLValue(ast[d].initializer!) + let lhs = ast[d].pattern - UNIMPLEMENTED() - } + assert(program[lhs].introducer.value.isConsuming || (storage == nil)) - /// Returns a basic block in which the names in `pattern` have been declared and initialized. - /// - /// This method method implements conditional narrowing for union types. - private mutating func emitConditionalNarrowing( - _ subject: Operand, typed union: UnionType, - as pattern: BindingPattern.ID, typed patternType: AnyType, - to storage: Operand, - else failure: Block.ID, in scope: AnyScopeID - ) -> Block.ID { - // TODO: Implement narrowing to an arbitrary subtype. - guard union.elements.contains(patternType) else { UNIMPLEMENTED() } - let site = ast[pattern].site + if let rhsType = UnionType(module.type(of: rhs).ast) { + guard rhsType.elements.contains(lhsType) else { UNIMPLEMENTED("recursive narrowing") } - let next = appendBlock(in: scope) - var targets = UnionSwitch.Targets( - union.elements.map({ (e) in (key: e, value: failure) }), - uniquingKeysWith: { (a, _) in a }) - targets[patternType] = next + let next = appendBlock(in: scope) + let site = program[lhs].site - insert(module.makeUnionSwitch(on: subject, toOneOf: targets, at: site)) - insertionPoint = .end(of: next) - let x0 = insert(module.makeOpenUnion(subject, as: patternType, at: site))! - pushing(Frame()) { (this) in - this.emitMove([.set], x0, to: storage, at: site) - } - insert(module.makeCloseUnion(x0, at: site)) + var targets = UnionSwitch.Targets( + rhsType.elements.map({ (e) in (key: e, value: failure) }), + uniquingKeysWith: { (a, _) in a }) + targets[lhsType] = next + emitUnionSwitch(on: rhs, toOneOf: targets, at: site) - emitLocalDeclarations(introducedBy: pattern, referringTo: [], relativeTo: storage) + insertionPoint = .end(of: next) - return next + if let target = storage { + let x0 = insert(module.makeAccess(.sink, from: rhs, at: site))! + let x1 = insert(module.makeOpenUnion(x0, as: lhsType, at: site))! + emitMove([.set], x1, to: target, at: site) + emitLocalDeclarations(introducedBy: lhs, referringTo: [], relativeTo: target) + insert(module.makeCloseUnion(x1, at: site)) + insert(module.makeEndAccess(x0, at: site)) + } else { + let k = AccessEffect(program[lhs].introducer.value) + let x0 = insert(module.makeAccess(k, from: rhs, at: site))! + let x1 = insert(module.makeOpenUnion(x0, as: lhsType, at: site))! + assignProjections(of: x1, to: program[d].pattern) + } + + return next + } else { + UNIMPLEMENTED() + } } /// Inserts the IR for branch condition `e`. @@ -3077,7 +3068,7 @@ struct Emitter { let targets = UnionSwitch.Targets( t.elements.map({ (e) in (key: e, value: appendBlock()) }), uniquingKeysWith: { (a, _) in a }) - insert(module.makeUnionSwitch(on: storage, toOneOf: targets, at: site)) + emitUnionSwitch(on: storage, toOneOf: targets, at: site) let tail = appendBlock() for (u, b) in targets { @@ -3187,7 +3178,7 @@ struct Emitter { insert(module.makeCondBranch(if: x0, then: same, else: fail, at: site)) insertionPoint = .end(of: same) - insert(module.makeUnionSwitch(on: lhs, toOneOf: targets, at: site)) + emitUnionSwitch(on: lhs, toOneOf: targets, at: site) for (u, b) in targets { insertionPoint = .end(of: b) let y0 = insert(module.makeOpenUnion(lhs, as: u, at: site))! @@ -3299,6 +3290,16 @@ struct Emitter { return x1 } + /// Appends the IR for jumping to the block assigned to the type of `scrutinee`'s payload in + /// `targets`. + private mutating func emitUnionSwitch( + on scrutinee: Operand, toOneOf targets: UnionSwitch.Targets, at site: SourceRange + ) { + let u = UnionType(module.type(of: scrutinee).ast)! + let i = emitUnionDiscriminator(scrutinee, at: site) + insert(module.makeUnionSwitch(over: i, of: u, toOneOf: targets, at: site)) + } + /// Returns the result of calling `action` on a copy of `self` in which a `newFrame` is the top /// frame. /// diff --git a/Sources/IR/InstructionTransformer.swift b/Sources/IR/InstructionTransformer.swift index c5c7bd74f..f0e7a9540 100644 --- a/Sources/IR/InstructionTransformer.swift +++ b/Sources/IR/InstructionTransformer.swift @@ -240,12 +240,13 @@ extension IR.Program { } case let s as UnionSwitch: - let x0 = t.transform(s.scrutinee, in: &self) - let x1 = s.targets.reduce(into: UnionSwitch.Targets()) { (d, kv) in + let x0 = t.transform(s.discriminator, in: &self) + let x1 = UnionType(t.transform(^s.union, in: &self))! + let x2 = s.targets.reduce(into: UnionSwitch.Targets()) { (d, kv) in _ = d[t.transform(kv.key, in: &self)].setIfNil(t.transform(kv.value, in: &self)) } return insert(at: p, in:n) { (target) in - target.makeUnionSwitch(on: x0, toOneOf: x1, at: s.site) + target.makeUnionSwitch(over: x0, of: x1, toOneOf: x2, at: s.site) } case let s as Unreachable: diff --git a/Sources/IR/Operands/Instruction/UnionSwitch.swift b/Sources/IR/Operands/Instruction/UnionSwitch.swift index c4ce077eb..849bc9589 100644 --- a/Sources/IR/Operands/Instruction/UnionSwitch.swift +++ b/Sources/IR/Operands/Instruction/UnionSwitch.swift @@ -7,8 +7,11 @@ public struct UnionSwitch: Terminator { /// The type of a map from payload type to its target. public typealias Targets = OrderedDictionary - /// The union container whose discriminator is read. - public private(set) var scrutinee: Operand + /// The discriminator of the union container over which the instruction switches. + public private(set) var discriminator: Operand + + /// The type of the union over which the instruction switches. + public let union: UnionType /// A map from payload type to its target. public private(set) var targets: Targets @@ -17,8 +20,9 @@ public struct UnionSwitch: Terminator { public let site: SourceRange /// Creates an instance with the given properties. - fileprivate init(scrutinee: Operand, targets: Targets, site: SourceRange) { - self.scrutinee = scrutinee + fileprivate init(discriminator: Operand, union: UnionType, targets: Targets, site: SourceRange) { + self.discriminator = discriminator + self.union = union self.targets = targets self.site = site } @@ -26,7 +30,7 @@ public struct UnionSwitch: Terminator { public var operands: [Operand] { - [scrutinee] + [discriminator] } public var successors: [Block.ID] { @@ -35,7 +39,7 @@ public struct UnionSwitch: Terminator { public mutating func replaceOperand(at i: Int, with new: Operand) { precondition(i == 0) - scrutinee = new + discriminator = new } mutating func replaceSuccessor(_ old: Block.ID, with new: Block.ID) -> Bool { @@ -51,7 +55,7 @@ public struct UnionSwitch: Terminator { extension UnionSwitch: CustomStringConvertible { public var description: String { - var s = "union_switch \(scrutinee)" + var s = "union_switch \(discriminator)" for (t, b) in targets { s.write(", \(t) => \(b)") } @@ -62,21 +66,21 @@ extension UnionSwitch: CustomStringConvertible { extension Module { - /// Creates a `union_switch` anchored at `site` that jumps to the block assigned to the type of - /// `scrutinee`'s payload in `targets`. + /// Creates a `union_switch` anchored at `site` that switches over `discriminator`, which is the + /// discriminator of a container of type `union`, jumping to corresponding block in `target`. + /// + /// If `union` is generic, `discriminator` should be the result of `union_discriminator` rather + /// than a constant. /// - /// - Requires: `scrutinee` is a union container and `targets` has a key defined for each of the - /// elements in scrutinee's type. + /// - Requires: `targets` has a key defined for each of `union`. func makeUnionSwitch( - on scrutinee: Operand, toOneOf targets: UnionSwitch.Targets, at site: SourceRange + over discriminator: Operand, of union: UnionType, toOneOf targets: UnionSwitch.Targets, + at site: SourceRange ) -> UnionSwitch { - let t = type(of: scrutinee) - guard t.isAddress, let u = UnionType(t.ast) else { - preconditionFailure("invalid type '\(t)'") - } - precondition(u.elements.allSatisfy({ (e) in targets[e] != nil })) - - return .init(scrutinee: scrutinee, targets: targets, site: site) + let t = type(of: discriminator) + precondition(t.isObject && t.ast.isBuiltinInteger) + precondition(union.elements.allSatisfy({ (e) in targets[e] != nil })) + return .init(discriminator: discriminator, union: union, targets: targets, site: site) } } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo index 1c706bef6..725d934cf 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/Int32.hylo @@ -308,7 +308,7 @@ public conformance Int32: FixedWidthInteger { public fun infix&<< (_ n: Int) -> Self { var lhs = self.copy() - lhs &<<= n + &lhs &<<= n return lhs } @@ -318,7 +318,7 @@ public conformance Int32: FixedWidthInteger { public fun infix&>> (_ n: Int) -> Self { var lhs = self.copy() - lhs &>>= n + &lhs &>>= n return lhs } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo index 5618934b9..9f816b2cf 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/Int64.hylo @@ -309,7 +309,7 @@ public conformance Int64: FixedWidthInteger { public fun infix&<< (_ n: Int) -> Self { var lhs = self.copy() - lhs &<<= n + &lhs &<<= n return lhs } @@ -319,7 +319,7 @@ public conformance Int64: FixedWidthInteger { public fun infix&>> (_ n: Int) -> Self { var lhs = self.copy() - lhs &>>= n + &lhs &>>= n return lhs } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/Int8.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/Int8.hylo index 9ea05de50..02b6dd122 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/Int8.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/Int8.hylo @@ -314,7 +314,7 @@ public conformance Int8: FixedWidthInteger { public fun infix&<< (_ n: Int) -> Self { var lhs = self.copy() - lhs &<<= n + &lhs &<<= n return lhs } @@ -324,7 +324,7 @@ public conformance Int8: FixedWidthInteger { public fun infix&>> (_ n: Int) -> Self { var lhs = self.copy() - lhs &>>= n + &lhs &>>= n return lhs } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo index 819e60d48..842419dfe 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/UInt32.hylo @@ -267,7 +267,7 @@ public conformance UInt32: FixedWidthInteger { public fun infix&<< (_ n: Int) -> Self { var lhs = self.copy() - lhs &<<= n + &lhs &<<= n return lhs } @@ -277,7 +277,7 @@ public conformance UInt32: FixedWidthInteger { public fun infix&>> (_ n: Int) -> Self { var lhs = self.copy() - lhs &>>= n + &lhs &>>= n return lhs } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo index 020d35778..bdf77b206 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/UInt64.hylo @@ -268,7 +268,7 @@ public conformance UInt64: FixedWidthInteger { public fun infix&<< (_ n: Int) -> Self { var lhs = self.copy() - lhs &<<= n + &lhs &<<= n return lhs } @@ -278,7 +278,7 @@ public conformance UInt64: FixedWidthInteger { public fun infix&>> (_ n: Int) -> Self { var lhs = self.copy() - lhs &>>= n + &lhs &>>= n return lhs } diff --git a/StandardLibrary/Sources/Core/Numbers/Integers/UInt8.hylo b/StandardLibrary/Sources/Core/Numbers/Integers/UInt8.hylo index da8407db0..e6d505c68 100644 --- a/StandardLibrary/Sources/Core/Numbers/Integers/UInt8.hylo +++ b/StandardLibrary/Sources/Core/Numbers/Integers/UInt8.hylo @@ -273,7 +273,7 @@ public conformance UInt8: FixedWidthInteger { public fun infix&<< (_ n: Int) -> Self { var lhs = self.copy() - lhs &<<= n + &lhs &<<= n return lhs } @@ -283,7 +283,7 @@ public conformance UInt8: FixedWidthInteger { public fun infix&>> (_ n: Int) -> Self { var lhs = self.copy() - lhs &>>= n + &lhs &>>= n return lhs } diff --git a/Tests/EndToEndTests/TestCases/Concurrency/concurrent_sort.hylo b/Tests/EndToEndTests/TestCases/Concurrency/concurrent_sort.hylo index b843ba0f9..137c0f70d 100644 --- a/Tests/EndToEndTests/TestCases/Concurrency/concurrent_sort.hylo +++ b/Tests/EndToEndTests/TestCases/Concurrency/concurrent_sort.hylo @@ -138,7 +138,7 @@ type ArraySlice : Deinitializable, Movable { &elements.swap_at(i, i + 1) &swapped = true } - i += 1 + &i += 1 } } while swapped } diff --git a/Tests/EndToEndTests/TestCases/SyntheticCopy.hylo b/Tests/EndToEndTests/TestCases/SyntheticCopy.hylo index 791da7540..aca8ec226 100644 --- a/Tests/EndToEndTests/TestCases/SyntheticCopy.hylo +++ b/Tests/EndToEndTests/TestCases/SyntheticCopy.hylo @@ -12,11 +12,9 @@ public fun main() { var b = a.copy() &a.y = 1337 - let ax = if let x: Int = a.x { x } else { 0 } - precondition(ax == 42) + precondition(a.x == (42 as _)) precondition(a.y == 1337) - let bx = if let x: Int = b.x { x } else { 0 } - precondition(bx == 42) + precondition(b.x == (42 as _)) precondition(b.y == 1) } diff --git a/Tests/HyloTests/TestCases/Lowering/Narrowing.hylo b/Tests/HyloTests/TestCases/Lowering/Narrowing.hylo new file mode 100644 index 000000000..84e2ca347 --- /dev/null +++ b/Tests/HyloTests/TestCases/Lowering/Narrowing.hylo @@ -0,0 +1,18 @@ +//- lowerToFinishedIR expecting: .success + +fun use(_ x: T) {} + +public fun main() { + var x: Optional = 1 as _ + + if let y: Int = x { + // Additional access to `x` is legal. + use(x) + use(y) + } + + if sink let y: Int = x { + // Mutation of `x` is legal since it has been consumed. + &x = .none() + } +} diff --git a/Tests/LibraryTests/TestCases/MutableCollectionTests.hylo b/Tests/LibraryTests/TestCases/MutableCollectionTests.hylo index 0e5521726..a8afe9215 100644 --- a/Tests/LibraryTests/TestCases/MutableCollectionTests.hylo +++ b/Tests/LibraryTests/TestCases/MutableCollectionTests.hylo @@ -5,7 +5,7 @@ fun append_count(up_to limit: Int, to a: inout Array) { var i = 0 while i < limit { &a.append(i.copy()) - i += 1 + &i += 1 } } @@ -30,10 +30,10 @@ fun test_rotate(length N: Int) { if a[j] != (pivot + j) % N { &failed = true } - j += 1 + &j += 1 } - pivot += 1 + &pivot += 1 } precondition(!failed) } @@ -44,7 +44,7 @@ fun test_rotate() { var n = 0 while n < 17 { test_rotate(length: n) - n += 1 + &n += 1 } } diff --git a/Tests/LibraryTests/TestCases/RangeTests.hylo b/Tests/LibraryTests/TestCases/RangeTests.hylo index 3c49dda80..22e83b531 100644 --- a/Tests/LibraryTests/TestCases/RangeTests.hylo +++ b/Tests/LibraryTests/TestCases/RangeTests.hylo @@ -4,11 +4,11 @@ public fun test_conformance_to_iterator() { var r = 1 ..< 3 var x: Int - &x = if let y: Int = &r.next() { y } else { -1 } + &x = if sink let y: Int = &r.next() { y } else { -1 } precondition(x == 1) - &x = if let y: Int = &r.next() { y } else { -1 } + &x = if sink let y: Int = &r.next() { y } else { -1 } precondition(x == 2) - &x = if let y: Int = &r.next() { y } else { -1 } + &x = if sink let y: Int = &r.next() { y } else { -1 } precondition(x == -1) precondition(r.lower_bound == r.upper_bound)