Skip to content

Commit

Permalink
Ignore mutation markers during name resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
kyouko-taiga committed Aug 16, 2024
1 parent f46bc7d commit 96a7f7a
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 55 deletions.
60 changes: 25 additions & 35 deletions Sources/IR/Emitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1579,8 +1579,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) = emitFunctionCallee(ast[e].callee, markedForMutation: m)
let (callee, captures) = emitFunctionCallee(ast[e].callee)
let inputs = captures + arguments

// Call is evaluated last.
Expand Down Expand Up @@ -2068,8 +2067,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 @@ -2120,8 +2118,7 @@ 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.
///
/// Lifted arguments correspond to the captures of the `callee`, which are additional parameters
/// of the lowered function. Bound member functions have a single lifted argument denoting their
Expand All @@ -2130,22 +2127,21 @@ struct Emitter {
///
/// - Requires: `callee` has a lambda type.
private mutating func emitFunctionCallee(
_ callee: AnyExprID, markedForMutation isMutating: Bool
_ callee: AnyExprID
) -> (callee: Callee, captures: [Operand]) {
switch callee.kind {
case NameExpr.self:
return emitNamedFunctionCallee(.init(callee)!, markedForMutation: isMutating)
return emitNamedFunctionCallee(.init(callee)!)
case InoutExpr.self:
return emitFunctionCallee(ast[InoutExpr.ID(callee)!].subject, markedForMutation: true)
return emitFunctionCallee(ast[InoutExpr.ID(callee)!].subject)
default:
return (callee: .lambda(emitLambdaCallee(callee)), captures: [])
}
}

/// Inserts the IR for given `callee`, which is marked for mutation iff `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 emitNamedFunctionCallee(
_ callee: NameExpr.ID, markedForMutation isMutating: Bool
_ callee: NameExpr.ID
) -> (callee: Callee, captures: [Operand]) {
switch program[callee].referredDecl {
case .direct(let d, let a) where d.isCallable:
Expand All @@ -2158,7 +2154,7 @@ struct Emitter {

case .member(let d, _, _) where d.isCallable:
// Callee is a member reference; the receiver is the only capture.
return emitMemberFunctionCallee(callee, markedForMutation: isMutating)
return emitMemberFunctionCallee(callee)

case .builtinFunction, .builtinType:
// Calls to built-ins should have been handled already.
Expand All @@ -2171,19 +2167,17 @@ 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.
/// Inserts the IR evaluating `callee`, which refers to a member, returning the callee's value
/// along with the call receiver.
private mutating func emitMemberFunctionCallee(
_ callee: NameExpr.ID, markedForMutation isMutating: Bool
_ callee: NameExpr.ID
) -> (callee: Callee, captures: [Operand]) {
guard case .member(let d, let a, let s) = program[callee].referredDecl else { unreachable() }

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 request = receiverCapabilities(program[callee].type)
let entityToCall = module.memberCallee(
referringTo: d, memberOf: receiverType, accessedWith: request,
specializedBy: a, usedIn: program[callee].scope)
Expand Down Expand Up @@ -2233,26 +2227,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 +2253,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 +2269,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)
}

}

0 comments on commit 96a7f7a

Please sign in to comment.