Hi All (and in particular, SIL experts like @Slava_Pestov, @John_McCall, @Joe_Groff, and @Erik_Eckstein),
I'm working on the interpreter for the constexpr evaluator and am having trouble understanding how to propagate substitutions reliably between callers and callees. Our branch is still a couple months old (the merge will hopefully will be done soon), so we have an older version of the generics system than is currently on master, and notably still has SubstitutionList.
I have something that works in many many cases, but doesn't handle "non-generic substitutions" (I say that, but I don't really understand what a non-generic substitution is). Doug provided a nice testcase which I've further reduced to the code at the end of this post. The issue I'm facing is that when resolving witness table members, sometimes I resolve it down to a SILFunction that has fewer requirements, than the apply that is invoking it. Specifically, in the testcase below, I have this call to T(something:):
func substitutionsF<T: substitutionsP>(_: T.Type) -> T {
return T(something: substitutionsY())
}
Which is lowered into this SIL code:
%9 = witness_method $T, #substitutionsP.init!allocator.1 : <Self where Self : substitutionsP><T where T : substitutionsP> (Self.Type) -> (T) -> Self : $@convention(witness_method: substitutionsP) <τ_0_0 where τ_0_0 : substitutionsP><τ_1_0 where τ_1_0 : substitutionsP> (@in τ_1_0, @thick τ_0_0.Type) -> @out τ_0_0 // user: %10
%10 = apply %9<T, substitutionsY>(%0, %7, %3) : $@convention(witness_method: substitutionsP) <τ_0_0 where τ_0_0 : substitutionsP><τ_1_0 where τ_1_0 : substitutionsP> (@in τ_1_0, @thick τ_0_0.Type) -> @out τ_0_0
Note that the apply has two substitutions present. They alook like this:
T
(abstract_conformance protocol=substitutionsP)
Y
(normal_conformance type=substitutionsY protocol=substitutionsP
(value req=init(something:) witness=pound_assert.(file).substitutionsY.init(something:)@/Users/clattner/swift-base/swift/test/SILOptimizer/pound_assert.swift:93:3)
(value req=get() witness=pound_assert.(file).substitutionsY.get()@/Users/clattner/swift-base/swift/test/SILOptimizer/pound_assert.(lldb) 6:8))(lldb)
The problem is that this is getting resolved to this witness table thunk:
// protocol witness for substitutionsP.init<A>(something:) in conformance substitutionsX
sil private [transparent] [thunk] @$S12pound_assert14substitutionsXVAA0C1PA2aDP9somethingxqd___tcAaDRd__lufCTW : $@convention(witness_method: substitutionsP) <τ_0_0 where τ_0_0 : substitutionsP> (@in τ_0_0, @thick substitutionsX.Type) -> @out substitutionsX {
... but that this thunk only has one requirement. I attempt to build a SubstitutionMap using the substitution list from the apply instruction, using this (effectively) this call:
auto signature = fn->getLoweredFunctionType()->getGenericSignature();
if (signature)
substitutionMap = signature->getSubstitutionMap(substitutions);
However, this dies in the end of getSubstitutionMap() with this assert:
Assertion failed: (subs.empty() && "did not use all substitutions?!"), function getSubstitutionMap, ...
Beyond the assertion, I'm concerned that I'm not passing these things in the right order.
Ok, so coming back to the actual question :-), how does one go about matching up the substitutions in an apply instruction to the requirements expected by a SubstitutionMap? I need a SubstitutionMap whose keys are the generic types in the callee, but whose values are the constant propagated types that the constexpr evaluator is pushing around.
Thank you so much for any help on this, I've been banging my head against it for a long time...
-Chris
Here's the full testcase:
protocol substitutionsP {
init<T: substitutionsP>(something: T)
func get() -> Int
}
struct substitutionsX : substitutionsP {
var state : Int
init<T: substitutionsP>(something: T) {
state = something.get()
}
func get() -> Int {
return state
}
}
struct substitutionsY : substitutionsP {
init() {}
init<T: substitutionsP>(something: T) {
}
func get() -> Int {
return 123
}
}
func substitutionsF<T: substitutionsP>(_: T.Type) -> T {
return T(something: substitutionsY())
}
func testProto() {
#assert(substitutionsF(substitutionsX.self).get() == 123)
}