@Jack Huey I suppose we can pass the infer into that fn -- that's what you mean, right?

Yep exactly

Ok so I was able to move program_clauses to ContextOps. Did end up needing to make InferenceTable an associated type on Context, but I think it actually mirrors the other associated types well. Now I need to actually move infer back onto Strand

@nikomatsakis So I was able to move infer back to strand and remove CanonicalStrand, but a lot of tests are failing, so I didn't correctly account for canonicalization. But I looked again at rustc's use of Chalk, and it's InferenceTable has a lifetime, so I don't know if we can store InferenceTable in Strand anyways.

not sure specifically about GoalInEnvironment

well I guess that's the type we use for the "answer" to a query, maybe

And, I'm still trying to wrap my head around where things get canonicalized/uncanonicalized. When I removed CanonicalGoal, I sort of just assumed that I should just be able to pass around the CanonicalizedExClause (with accessing the inner ExClause), but that wasn't an accurate assumption. So seems like still need to more or less keep the canonicalization/uncanonicalization as things get passed around

Right, so I guess my question between UCanonicalGoalInEnvironment and CanonicalGoalInEnvironment is it is ever necessary to know that you're not the first universe? Like, if all canonicalization renumbered the universe to be minimal?

OK. I guess I would summarize it as:

canonicalization in general happens when you cross between inference contexts

this can happen when you start a new query, and when you produce a result
* when starting a new query, you also canonicalize universes
* when producing a result, you are referencing the universes from the start of the query

Ok, then when canonicalized X would be converted to ^0?

yes, because canonicalization basically adds a level of binder around the whole thing

Also, the only place that infer.canonicalize_goal is called is in abstract_positive_literal and abstract_negative_literal, but that immediately get's converted to a UCanonicalizedGoalInEvironment in get_or_create_table_for_subgoal

I'm struggling with how to convey this sense but basically

Thanks for going through this with me. Like I said, trying to wrap my head around it all

you have terms (types, etc) and they can either reference things from the surrounding environment ("free variables") or they can include a binder and reference back to that -- and there is some implicit point that we are maintaining always

in more conventional notation it would be like forall<T> { T: Debug }

These are debruijn indices right?

but when we want to try and prove that to be true, we "enter" the forall -- kind of shifting the "current point" between what is bound and free. So we create the next universe U and replace the bound variable (^0, or T) with !U, leaving us with a new term !U: Debug -- note that there is no forall here, it's in the environment now

Also, the only place that infer.canonicalize_goal is called is in abstract_positive_literal and abstract_negative_literal, but that immediately get's converted to a UCanonicalizedGoalInEvironment in get_or_create_table_for_subgoal

it may be that we don't need to have CanonicalizedGoalInEnvornment as a separate type

then when we start processing the query

we would convert that to !1: Foo

Ok so, taking a step back. What exactly is the goal of removing CanonicalStrand? Or, more-so, what's the benefit of storing infer in Strand? Is the intention that we pass a canonicalized strand around or the uncanonicalized strand then canonicalize where needed?

I guess I should probably go back to the commit that added CanonicalStrand and look at how it was used before

Ok so, taking a step back. What exactly is the goal of removing CanonicalStrand? Or, more-so, what's the benefit of storing infer in Strand? Is the intention that we pass a canonicalized strand around or the uncanonicalized strand then canonicalize where needed?

the benefit is not having to canonicalize when we suspend a strand and to "de-canonicalize" when we restore one

it's a purely a runtime optimization

perhaps also a (to my mind) conceptual simplification

in that I think you want to have one inference context per .. hmm .. "active environment" in which work is being done, and a strand is one such environment (similarly, the code in the compiler creates one inference context per function, etc)

Ok. So. I think there's some subtlety here that I'm not seeing. I have a feeling that it has to do with the InferenceTable now being persistent instead being re-created every time a CanonicalStrand get instantiated into a Strand. Either that, some state is getting shared when Strands are supposed to cloned fresh. Which is causing a bunch of tests to fail, and I honestly don't know why. So I think a better approach to this is to take a step back to the known-passing code, add some unit tests that specifically test behavior in chalk-engine, instead of only the integration tests in the root.