Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Simplify variant selection #1554

Merged
merged 3 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Sources/FrontEnd/TypeChecking/TypeChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
46 changes: 22 additions & 24 deletions Sources/IR/Emitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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]) {
Expand All @@ -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])
}
}
Expand Down Expand Up @@ -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<SubscriptDecl>, 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<SubscriptDecl>, captures: [Operand]) {
switch program[callee].referredDecl {
case .direct(let d, let a) where d.kind == SubscriptDecl.self:
Expand All @@ -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.
Expand All @@ -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<SubscriptDecl>, 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])
Expand Down
24 changes: 4 additions & 20 deletions Sources/IR/TypedProgram+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<SubscriptDecl> {
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)
}

}
19 changes: 19 additions & 0 deletions Tests/EndToEndTests/TestCases/MethodBundle.hylo
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}