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

feat: add purity checking to Verifier #7683

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

felix-berg
Copy link
Contributor

Currently many errors thrown. Many regarding combination of purities in Let expressions.

@felix-berg felix-berg marked this pull request as draft May 6, 2024 08:38
/**
* Throw `InternalCompilerException` because the `expected` purity does not match the `found` purity
*/
private def failUnexpectedPurity(found: Purity, expected: Purity, loc: SourceLocation): Nothing =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicated?

Copy link
Contributor Author

@felix-berg felix-berg May 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea... any suggestions for improvements? I thought that parameterizing type of found/expected was ugly.
maybe there is some elegant Scala-way?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, didn't see that the one used a string. maybe you could make one call the other?

/**
* Asserts that `p` is either `Impure` or `ControlImpure`
*/
private def checkImpure(p: Purity, loc: SourceLocation): Purity = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid ambiguous language I think i'd call it checkNotPure. This Impure/Control-impure is quite new so the words have not been totally decided

case Constant.Str(_) => check(expected = MonoType.String)(actual = tpe, loc)
case Constant.Regex(_) => check(expected = MonoType.Regex)(actual = tpe, loc)
}
(t, Purity.Pure)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its a bit weird with this AST node and some others, in that it does have a purity, just not in its "pattern" (see the definition of Expr.Cst). I think its the most consistent to check that it is pure, even though its defined as pure always.

case Expr.Cst(cst, tpe, loc) =>
val t = cst match {
case Constant.Unit => check(expected = MonoType.Unit)(actual = tpe, loc)
case Constant.Null => tpe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated note: in the jvm PR you have the power to check that tpe is an object type


op match {
case AtomicOp.Unary(sop) =>
val List(t) = ts
val (List(t), List(p)) = rs.unzip
val opTpe = sop match {
case SemanticOp.BoolOp.Not => MonoType.Bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could make sense to, for each case, return its effect. so every line would say (MonoType.Something, Purity.Pure) and the combine that with the result. It doesn't do anything but it makes it more robust to future modifications

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing in binary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, if it doesn't hurt the code to much, it would be nice that in a given expression it is clear what its inherent effect is and what the "nested" effect is

tpe
(
checkEq(arrType, tpe, loc),
checkEq(pur, Purity.combine3(pur, p1, p2), loc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think pur should be in the combination

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh its because we don't know the inherent effect of the array creating because we don't have the region anymore?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh and we cant assume impure because some code is cheating with Array[t, Pure] hmmm

case _ => failMismatchedShape(t1, "Ref", loc)
}

// Match- and Hole-errors match with any type
case AtomicOp.HoleError(_) =>
tpe
(tpe, pur)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i might be mistaken, but i think these error should always be pure

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i ran into some cases where Hole/MatchError's were impure.

val defn = root.defs(sym)
val signature = MonoType.Arrow(defn.fparams.map(_.tpe), defn.tpe)

val decl = MonoType.Arrow(defn.cparams.map(_.tpe), signature)
val actual = MonoType.Arrow(ts, tpe)

checkEq(decl, actual, loc)
tpe
(tpe, pur)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pur should still reflect the combination of all the rs effects


case AtomicOp.Region =>
check(expected = MonoType.Region)(actual = tpe, loc)
case AtomicOp.Region => // TODO: VERIFIER: purity checking for Region/Spawn
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

region itself is always a pure expression

val lamType1 = visitExpr(exp)
val lamType2 = MonoType.Arrow(exps.map(visitExpr), tpe)
case Expr.ApplyClo(exp, exps, ct, tpe, pur, loc) => // TODO: VERIFIER: purity checking for function calls/TryWith/Do/NewObject (+ a discussion about Purity in MonoType.Arrow?)
val lamType1 = visitExpr(exp)._1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ignored parts of visitExpr should also be combined and checked against pur. even without the arrow purity, we still know that the combination must be less than or equal to pur.

@JonathanStarup
Copy link
Contributor

Its looking good but you've found some complications we need to solve, am i missing something?

  • arrows do not have purity
  • arrays don't know their region, so verification is less precise

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants