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

"ProductionStmt-level case" (and if) #864

Open
remexre opened this issue Nov 23, 2024 · 2 comments
Open

"ProductionStmt-level case" (and if) #864

remexre opened this issue Nov 23, 2024 · 2 comments

Comments

@remexre
Copy link
Member

remexre commented Nov 23, 2024

Sometimes it would be nice to write:

production whatever
...
{
  case someComplicatedExpr(...) of
  | p(x, y, z) -> {
      local foo :: Integer = ...;
      top.bar = x + y;
      top.baz = z - foo;
    }
  | q(w) -> {
      -- foo must not be in scope here
      top.bar = 0;
      top.baz = w;
      top.errors <- ...;
    }
  end;
}

and have it forward to:

production whatever
...
{
  -- not needed if the scrutinee doesn't get its own thunk, e.g. if it's top.foo
  local scrutinee_0 :: Inferred = someComplicatedExpr(...);
  local foo :: Integer =
    case scrutinee_0 of
    | p(x, y, z) -> ...
    | _ -> error("plz file a bug on silver thx")
    end;
  top.bar =
    case scrutinee_0 of
    | p(x, y, z) -> x + y
    | q(w) -> 0
    end;
  top.baz =
    case scrutinee_0 of
    | p(x, y, z) -> z - foo
    | q(w) -> w
    end;
  top.errors <-
    case scrutinee_0 of
    | p(x, y, z) -> [] -- or whatever the monoid attr identity is for this attr
    | q(w) -> ...
    end;
}

I feel like we discussed this ages ago, but I didn't find an issue just now.

  • Locals can be declared anywhere, but are scoped within their own {}.
  • =/:= eqtns must appear on all branches if they appear on any; i think this could be handled on the forward tree tbh.
  • <- for monoid attributes can appear anywhere, they probably need to get collected up.
  • <- eqtns for non-monoid collection attributes must appear in every branch, but it should be fine if they appear a different number of times in each branch.
  • I have not thought about production attrs, propagate, or anything else not mentioned here.

Whoever's implementing this should probably do it for if as well; I think that's less useful, but it should be easy to do at the same time.

I think I'm gonna claim this is a good first issue for working on Silver itself? It should be able to be an extension that doesn't do much analysis, at least (I think).

@krame505
Copy link
Member

I like the idea here, this is something I've found myself wanting sometimes. This feels like a more natural approach than implicit monads, for writing attribute equations that are only valid in some contexts. The most scenarios for this are some version of an environment lookup, where we don't want to write a case for every equation that makes use of the lookup result, or some equations on some sort of type check succeeding.

We should probably push the pattern matching as deep in to the expressions as possible, to avoid unneeded data dependencies on sub-expressions that cause circularites and limit the use of sharing. For example,

production foo
top::Expr ::= e::Expr
{
  case top.countIn of
  | just(i) -> {
    local trans::Expr = arrayIndex(@e, intConst(i));
    trans.env = top.env;
    top.typerep = trans.typerep;
  } | _ -> {
    top.typerep = errorType();
  }
}

should translate to something like

production foo
top::Expr ::= e::Expr
{
  local trans::Expr =
    arrayIndex(@e, intConst(case top.countIn of just(i) -> i | _ -> error("match failed") end));
  trans.env = top.env;
  top.typerep = case top.countIn of just(i) -> trans.typerep | _ -> errorType() end;
}

and not

production foo
top::Expr ::= e::Expr
{
  local trans::Expr =
    case top.countIn of
    | just(i) -> arrayIndex(@e, intConst(i))
    | _ -> error("match failed")
    end;
  trans.env = top.env;
  top.typerep = case top.countIn of just(i) -> trans.typerep | _ -> errorType() end;
}

This also makes tree sharing with a conditional forward to an error production a bit cleaner:

production foo
top::Expr ::= e::Expr
{
  local localErrors::[Message] = e.errors ++ ...;
  if null(localErrors) {
    forwards to bar(baz(@e));
  } else {
    forwards to errorExpr(localErrors);
  }
}

which would translate to something like

production foo
top::Expr ::= e::Expr
{
  local localErrors::[Message] = e.errors ++ ...;
  forward fwrd1 = bar(baz(@e));
  forward fwrd2 = errorExpr(localErrors);
  forwards to if null(localErrors) then @fwrd1 else @fwrd2;
}

The main question I have is how aspects should be handled. It seems that we should be able to declare production attributes in condition bodies, but there would need to be some way of aspecting a particular condition for them to be in scope. Perhaps conditions should be named? This would also avoid name collisions between locals with the same name in different conditions:

production var
top::Expr ::= n::Name
{
  cond lookupSuccess when lookup(n.name, top.env) matches item :: _ {
    production ty::TypeScheme = item.typeScheme;
  }
  else lookupFail {
    top.errors <- [err(n, "Undefined value ${n.name}")]
  }
}
...
aspect production var
top::Expr ::= n::Name
{
  aspect lookupSuccess {
    top.typerep = ty.typerep;
  }
  aspect lookupFail {
    top.typerep = errorType();
  }
}

I'm not sure I love that syntax, though.

@krame505
Copy link
Member

I'm also now realizing that this feature is largely redundant with dispatch productions. For type checking / environment lookups, you can structure the main production to forward to either an error production, or an implementation production with the lookup result:

production var
top::Expr ::= n::Name
{
  n.env = top.env;
  forwards to
    case lookup(n.name, top.env) of
    | item :: _ -> foundVar(item)
    | _ -> errorVar()
    end(@n);
    production ty::TypeScheme = item.typeScheme;
  }
}
dispatch Var = Expr ::= @n::Name;
production foundVar
top::Expr ::= @n::Name item::ValueItem
{
  production ty::TypeScheme = item.typeScheme;
}
production errorVar
top::Expr ::= @n::Name
{
  top.errors <- [err(n, "Undefined value ${n.name}")]
}
...
aspect typerep on Expr of
| foundVar(n, item) -> ty.typerep
| errorVar() -> errorType
end;

@github-project-automation github-project-automation bot moved this to To do - uncategorized in Hackathon Nov 26, 2024
@krame505 krame505 moved this from To do - uncategorized to To do - standalone in Hackathon Nov 26, 2024
@krame505 krame505 moved this from To do - standalone to To do - uncategorized in Hackathon Jan 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: To do
Development

No branches or pull requests

2 participants