From d73d901ff63481223ed3dae332eed89a5d9fe903 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 16 Aug 2024 13:45:50 +0200 Subject: [PATCH 1/3] Ignore mutation markers on subscript calls during name resolution --- Sources/IR/Emitter.swift | 46 ++++++++++++------------ Sources/IR/TypedProgram+Extensions.swift | 24 +++---------- 2 files changed, 26 insertions(+), 44 deletions(-) diff --git a/Sources/IR/Emitter.swift b/Sources/IR/Emitter.swift index bf6850869..d4978bd2b 100644 --- a/Sources/IR/Emitter.swift +++ b/Sources/IR/Emitter.swift @@ -2068,8 +2068,7 @@ struct Emitter { let arguments = emitArguments( to: ast[e].callee, in: CallID(e), usingExplicit: ast[e].arguments, synthesizingDefaultAt: .empty(at: ast[e].site.end)) - let m = ast.isMarkedForMutation(ast[e].callee) - let (callee, captures) = emitSubscriptCallee(ast[e].callee, markedForMutation: m) + let (callee, captures) = emitSubscriptCallee(ast[e].callee) return (callee, captures + arguments) } @@ -2172,7 +2171,7 @@ struct Emitter { } /// Inserts the IR evaluating `callee`, which refers to a member function marked for mutation - /// iff `isMutating` is `true`, returning the callee's value along with the call receiver. + /// iff `isMutating` is `true`, returning the callee's value along with the call receiver. private mutating func emitMemberFunctionCallee( _ callee: NameExpr.ID, markedForMutation isMutating: Bool ) -> (callee: Callee, captures: [Operand]) { @@ -2181,17 +2180,20 @@ struct Emitter { let receiver = emitLValue(receiver: s, at: ast[callee].site) let receiverType = module.type(of: receiver).ast - let request = program.requestedCapabilities( - onBundleProviding: receiverCapabilities(program[callee].type), - forInPlaceMutation: isMutating) + let available = receiverCapabilities(program[callee].type) + var requested = available.intersection(.forUseOfBundle(performingInPlaceMutation: isMutating)) + + // TODO: Should report an error when available is `let|sink` and requested is `inout/set` + requested = requested.isEmpty ? available : requested + let entityToCall = module.memberCallee( - referringTo: d, memberOf: receiverType, accessedWith: request, + referringTo: d, memberOf: receiverType, accessedWith: requested, specializedBy: a, usedIn: program[callee].scope) if case .bundle(let b) = entityToCall { return emitMethodBundleCallee(referringTo: b, on: receiver, at: program[callee].site) } else { - let c = insert(module.makeAccess(request, from: receiver, at: program[callee].site))! + let c = insert(module.makeAccess(requested, from: receiver, at: program[callee].site))! return (callee: entityToCall, captures: [c]) } } @@ -2233,26 +2235,24 @@ struct Emitter { } } - /// Inserts the IR for given `callee`, which is marked for mutation if `isMutating` is `true`, - /// and returns the callee's value along with its lifted arguments. + /// Inserts the IR for given `callee` and returns its value along with its lifted arguments. private mutating func emitSubscriptCallee( - _ callee: AnyExprID, markedForMutation isMutating: Bool + _ callee: AnyExprID ) -> (callee: BundleReference, captures: [Operand]) { // TODO: Handle captures switch callee.kind { case NameExpr.self: - return emitNamedSubscriptCallee(.init(callee)!, markedForMutation: isMutating) + return emitNamedSubscriptCallee(.init(callee)!) case InoutExpr.self: - return emitSubscriptCallee(ast[InoutExpr.ID(callee)!].subject, markedForMutation: true) + return emitSubscriptCallee(ast[InoutExpr.ID(callee)!].subject) default: UNIMPLEMENTED("call to an anonymous subscript of an rvalue") } } - /// Inserts the IR for given `callee`, which is marked for mutation if `isMutating` is `true`, - /// and returns the callee's value along with its lifted arguments. + /// Inserts the IR for given `callee` and returns its value along with its lifted arguments. private mutating func emitNamedSubscriptCallee( - _ callee: NameExpr.ID, markedForMutation isMutating: Bool + _ callee: NameExpr.ID ) -> (callee: BundleReference, captures: [Operand]) { switch program[callee].referredDecl { case .direct(let d, let a) where d.kind == SubscriptDecl.self: @@ -2261,13 +2261,12 @@ struct Emitter { UNIMPLEMENTED("subscript with non-empty environment") } - let entityToCall = program.subscriptBundleReference( - to: .init(d)!, specializedBy: a, markedForMutation: isMutating) + let entityToCall = program.subscriptBundleReference(to: .init(d)!, specializedBy: a) return (entityToCall, []) case .member(let d, _, _) where d.kind == SubscriptDecl.self: // Callee is a member reference; the receiver is the only capture. - return emitMemberSubscriptCallee(callee, markedForMutation: isMutating) + return emitMemberSubscriptCallee(callee) case .builtinFunction, .builtinType: // There are no built-in subscripts. @@ -2278,15 +2277,14 @@ struct Emitter { } } - /// Inserts the IR evaluating `callee`, which refers to a member subscript marked for mutation - /// iff `isMutating` is `true`, returning the callee's value along with the call receiver. + /// Inserts the IR evaluating `callee`, which refers to a member subscript, returning the + /// callee's value along with the call receiver. private mutating func emitMemberSubscriptCallee( - _ callee: NameExpr.ID, markedForMutation isMutating: Bool + _ callee: NameExpr.ID ) -> (callee: BundleReference, captures: [Operand]) { guard case .member(let d, let a, let s) = program[callee].referredDecl else { unreachable() } - let entityToCall = program.subscriptBundleReference( - to: .init(d)!, specializedBy: a, markedForMutation: isMutating) + let entityToCall = program.subscriptBundleReference(to: .init(d)!, specializedBy: a) let r = emitLValue(receiver: s, at: ast[callee].site) let c = insert(module.makeAccess(entityToCall.capabilities, from: r, at: ast[callee].site))! return (entityToCall, [c]) diff --git a/Sources/IR/TypedProgram+Extensions.swift b/Sources/IR/TypedProgram+Extensions.swift index 5cc7b26b6..60da0bb3c 100644 --- a/Sources/IR/TypedProgram+Extensions.swift +++ b/Sources/IR/TypedProgram+Extensions.swift @@ -28,28 +28,12 @@ extension TypedProgram { } } - /// Returns a subscript bundle reference to `d`, which occurs specialized by `z` and is marked - /// for mutation iff `isMutating` is `true`. + /// Returns a subscript bundle reference to `d`, which occurs specialized by `z`. func subscriptBundleReference( - to d: SubscriptDecl.ID, specializedBy z: GenericArguments, markedForMutation isMutating: Bool + to d: SubscriptDecl.ID, specializedBy z: GenericArguments ) -> BundleReference { - let t = SubscriptType(canonical(self[d].type, in: self[d].scope))! - let r = requestedCapabilities( - onBundleProviding: t.capabilities, forInPlaceMutation: isMutating) - return BundleReference(to: d, specializedBy: z, requesting: r) - } - - /// Returns the capabilities potentially requested by an access on a subscript or method bundle - /// defining `available`, used for mutation iff `m` is `true`. - func requestedCapabilities( - onBundleProviding available: AccessEffectSet, forInPlaceMutation m: Bool - ) -> AccessEffectSet { - let requested = available.intersection( - AccessEffectSet.forUseOfBundle(performingInPlaceMutation: m)) - - // TODO: requested is empty iff the program is ill-typed w.r.t. mutation markers - // assert(!requested.isEmpty) - return requested.isEmpty ? available : requested + let available = SubscriptType(canonical(self[d].type, in: self[d].scope))!.capabilities + return BundleReference(to: d, specializedBy: z, requesting: available) } } From 23c6ee0167c42647774053521d1e96176e2dda42 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 16 Aug 2024 14:11:56 +0200 Subject: [PATCH 2/3] Remove needless newlines --- Sources/FrontEnd/TypeChecking/TypeChecker.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/FrontEnd/TypeChecking/TypeChecker.swift b/Sources/FrontEnd/TypeChecking/TypeChecker.swift index 4238c64bf..8171e4102 100644 --- a/Sources/FrontEnd/TypeChecking/TypeChecker.swift +++ b/Sources/FrontEnd/TypeChecking/TypeChecker.swift @@ -5801,9 +5801,8 @@ struct TypeChecker { ) -> AnyType { switch callee.kind { case InoutExpr.self: - let e = InoutExpr.ID(callee)! - let t = inferredType( - ofCallee: program[e].subject, usedAs: purpose, implicitlyIn: q, updating: &obligations) + let e = program[InoutExpr.ID(callee)!].subject.id + let t = inferredType(ofCallee: e, usedAs: purpose, implicitlyIn: q, updating: &obligations) return constrain(callee, to: t, in: &obligations) case NameExpr.self: From be0f6b21e2da6c51e498d60d2f89e0b6f661f3f9 Mon Sep 17 00:00:00 2001 From: Dimi Racordon Date: Fri, 16 Aug 2024 15:37:21 +0200 Subject: [PATCH 3/3] Test method bundles providing 'let' and 'inout' variants --- .../EndToEndTests/TestCases/MethodBundle.hylo | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Tests/EndToEndTests/TestCases/MethodBundle.hylo b/Tests/EndToEndTests/TestCases/MethodBundle.hylo index 6c458a127..80cb2c708 100644 --- a/Tests/EndToEndTests/TestCases/MethodBundle.hylo +++ b/Tests/EndToEndTests/TestCases/MethodBundle.hylo @@ -13,8 +13,27 @@ type A: Deinitializable { } +public type B: Deinitializable { + + public let x: Int + + public memberwise init + + public fun foo(_ n: sink Int) { + let { B(x: n) } + inout { &self.x = n } + } + +} + public fun main() { var a = A(x: 42) precondition(a.foo().1 == 42) precondition(a.foo().1 == 42) + + var b0 = B(x: 1) + let b1 = b0.foo(2) + precondition(b1.x == 2) + &b0.foo(3) + precondition(b0.x == 3) }