Skip to content

Commit 7f9a458

Browse files
committed
Adapt discussed changes
- Start synthesizing all show definitions, not just missing ones
1 parent 1775052 commit 7f9a458

File tree

1 file changed

+159
-97
lines changed
  • effekt/shared/src/main/scala/effekt/core

1 file changed

+159
-97
lines changed

effekt/shared/src/main/scala/effekt/core/Show.scala

Lines changed: 159 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -15,96 +15,90 @@ import scala.annotation.targetName
1515
import effekt.core.Type.TString
1616
import effekt.source.FeatureFlag
1717
import effekt.generator.llvm.Transformer.llvmFeatureFlags
18+
import effekt.core.ExternBody.StringExternBody
19+
import effekt.core.ExternBody.Unsupported
1820

1921
object Show extends Phase[CoreTransformed, CoreTransformed] {
2022

2123
override val phaseName: String = "show"
2224

23-
case class ShowContext(showDefnsMap: Map[List[ValueType], Block.BlockVar], groundTypes: List[ValueType], var defnsToGenerate: collection.mutable.Map[ValueType, Id], infixConcatFn: Block.BlockVar)
25+
case class ShowContext(showDefns: collection.mutable.Map[ValueType, Id], backend: String, externFns: Map[String, Extern.Def]) {
26+
def getShowBlockVar(vt: ValueType): Block.BlockVar =
27+
val showId = showDefns(vt)
28+
Block.BlockVar(showId, BlockType.Function(List.empty, List.empty, List(vt), List.empty, TString), Set.empty)
29+
30+
def externBlockVar(name: String): Block.BlockVar =
31+
val fn = externFns(name)
32+
Block.BlockVar(fn.id, BlockType.Function(fn.tparams, fn.cparams, fn.vparams map (_.tpe), fn.bparams map (_.tpe), fn.ret), fn.annotatedCapture)
33+
}
2434

2535
override def run(input: CoreTransformed)(using Context): Option[CoreTransformed] = input match {
2636
case CoreTransformed(source, tree, mod, core) => {
27-
28-
// Collect all extern definitions that are named "show"
29-
// Record what type of values these functions take as arguments
30-
// Store these in a Map so we know our ground types
31-
// Map[Int -> show#1, Char -> show#2, ...]
32-
val extShowDefns: List[Extern.Def] = core.externs.flatMap(ext => ext match
33-
case e: Extern.Def => List(e)
34-
case _ => List()
37+
// 1. Find ids of relevant extern functions by name
38+
val externFnNames = List(
39+
"myshow",
40+
"infixConcat",
41+
"c_bytearray_show_Int",
42+
"c_bytearray_show_Byte",
43+
"c_bytearray_show_Double",
3544
)
36-
// TODO: Could be more specific in filtering here
37-
// for example only allow functions with one argument
38-
// which return Strings
39-
.filter(defn => defn.id.name.name == "show")
40-
41-
val myshow = core.externs.flatMap(ext => ext match
42-
case e: Extern.Def => List(e)
43-
case _ => List()
45+
val externFns = findRelevantExterns(core.externs, externFnNames)
46+
47+
// 2. Define list of base types to synthesize definitions for
48+
// 2.1 actual base types (Int, Char, ...)
49+
var builtinTypes = Type.Builtins
50+
// 2.2 data types without tparams
51+
val declarationTypes = core.declarations.flatMap(decl => decl match
52+
case Data(id, List(), constructors) => Some(decl)
53+
case _ => None
4454
)
45-
.filter(defn => defn.id.name.name == "myshow")
46-
47-
val blockVars: List[Block.BlockVar] = extShowDefns.map(defn =>
48-
Block.BlockVar(defn.id, BlockType.Function(defn.tparams, defn.cparams, defn.vparams map (_.tpe), defn.bparams map (_.tpe), defn.ret), Set.empty))
49-
val showDefnsMap: Map[List[ValueType], Block.BlockVar] = extShowDefns.zip(blockVars).map((defn, bv) =>
50-
(defn.vparams.map(_.tpe), bv)).toMap
51-
52-
53-
// TODO: This already assumes there is only one vparam
54-
val extGroundTypes = extShowDefns.map(defn => defn.vparams.map(_.tpe)(0))
55-
56-
// TODO: We also want to include declarations with targs
57-
// if those are called with ground types or types we already have generated a show instance for
58-
val declGroundTypes = core.declarations.flatMap(decl => decl match {
59-
case Data(id, List(), constructors) =>
60-
List(ValueType.Data(id, List.empty))
61-
case _ => List()
62-
})
63-
val groundTypes = extGroundTypes ++ declGroundTypes
64-
65-
// TODO: There has to be a better way... right?
66-
val infixConcatDef = core.externs.find(ext => ext match {
67-
case Extern.Def(id, tparams, cparams, vparams, bparams, ret, annotatedCapture, body) if id.name.name == "infixConcat" =>
68-
true
69-
case _ => false
70-
}).get
71-
var infixConcatFn: Block.BlockVar = infixConcatDef match {
72-
case Extern.Def(id, tparams, cparams, vparams, bparams, ret, annotatedCapture, body) =>
73-
BlockVar(id, BlockType.Function(tparams, cparams, vparams map (_.tpe), bparams map (_.tpe), ret), Set())
74-
case _ => sys error "Does not happen"
55+
// 2.3 data types with targs (later fixed by Mono)
56+
// 2.4 externs (hard, if not impossible)
57+
// 2.5 Types with user defined show definitions (skip for now, unclear if we even want this)
58+
59+
// 3. Synthesize `show` definitions, create (ValueType -> (show) Id) map for context
60+
// 3.1 figure out which backend we are generating for
61+
val backend: String = externFns("myshow").body match {
62+
case StringExternBody(featureFlag: FeatureFlag.NamedFeatureFlag, contents) => featureFlag.id
63+
case _ => sys error "Should never happen"
7564
}
65+
implicit val ctx: ShowContext = ShowContext(collection.mutable.Map.empty, backend, externFns)
7666

77-
implicit val ctx: ShowContext = ShowContext(showDefnsMap, groundTypes, collection.mutable.Map.empty, infixConcatFn)
67+
// 3.2 generate show instances for base types in that backend
68+
val baseShowInstances = generateShowInstancesBases(builtinTypes)
69+
// 3.3 generate show instances for declarations
70+
val declShowInstances = generateShowInstancesDecls(declarationTypes)
7871

79-
var transformed = transform(core)
72+
// var transformed = transform(core)
73+
// println(util.show(transformed))
74+
// Some(CoreTransformed(source, tree, mod, transformed))
8075

81-
println(util.show(transformed))
82-
83-
Some(CoreTransformed(source, tree, mod, transformed))
76+
None
8477
}
8578
}
8679

80+
def findRelevantExterns(externs: List[Extern], names: List[String]): Map[String, Extern.Def] =
81+
var retMap = Map[String, Extern.Def]()
82+
externs.foreach({
83+
case defn@Extern.Def(id, tparams, cparams, vparams, bparams, ret, annotatedCapture, body) if names.contains(id.name.name) =>
84+
retMap += (id.name.name -> defn)
85+
case _ => ()
86+
})
87+
retMap
88+
8789
def transform(decl: ModuleDecl)(using ctx: ShowContext): ModuleDecl = decl match {
8890
case ModuleDecl(path, includes, declarations, externs, definitions, exports) =>
8991
val transformedDefns = definitions map transform
90-
val additionalDefns = generateShowInstances(declarations)
9192
// Remove myshow extern
9293
// because it only has a dummy implementation which will fail when run
9394
val filteredExterns = decl.externs.flatMap(ext => ext match
9495
case e: Extern.Def => List(e)
9596
case _ => List()
9697
)
9798
.filter(defn => defn.id.name.name != "myshow")
98-
ModuleDecl(path, includes, declarations, filteredExterns, transformedDefns ++ additionalDefns, exports)
99+
ModuleDecl(path, includes, declarations, filteredExterns, transformedDefns, exports)
99100
}
100101

101-
// Q: May externs have show instances on any type
102-
// Ignoring these for now
103-
// def transform(extern: Extern)(using ShowContext): Extern = extern match {
104-
// case Extern.Def(id, tparams, cparams, vparams, bparams, ret, annotatedCapture, body) => ???
105-
// case Extern.Include(featureFlag, contents) => Extern.Include(featureFlag, contents)
106-
// }
107-
108102
def transform(toplevel: Toplevel)(using ShowContext): Toplevel = toplevel match {
109103
case Toplevel.Def(id, block) => Toplevel.Def(id, transform(block))
110104
case Toplevel.Val(id, tpe, binding) => ???
@@ -117,15 +111,11 @@ object Show extends Phase[CoreTransformed, CoreTransformed] {
117111
case Unbox(pure) => ???
118112
}
119113

120-
def transform(stmt: Stmt)(using ShowContext): Stmt =
114+
def transform(stmt: Stmt)(using ctx: ShowContext): Stmt =
121115
stmt match {
122116
case Let(id, annotatedTpe, PureApp(BlockVar(bid, bannotatedTpe, bannotatedCapt), targs, vargs), body) if bid.name.name == "myshow" =>
123117
if (targs.length != 1) sys error "expected targs to only have one argument"
124-
val targ = targs(0)
125-
if (isGroundType(targ))
126-
val bvar = getOrAddShow(targ)
127-
return Stmt.Val(id, annotatedTpe, Stmt.App(bvar, List.empty, vargs, List.empty), transform(body))
128-
sys error "targ was not ground type in PureApp"
118+
Stmt.Val(id, annotatedTpe, Stmt.App(ctx.getShowBlockVar(targs(0)), List.empty, vargs, List.empty), transform(body))
129119
case Let(id, annotatedTpe, binding, body) => Let(id, annotatedTpe, transform(binding), transform(body))
130120
case Return(expr) => Return(transform(expr))
131121
// TODO: We might need to do the same thing as in PureApp if we want to allow show(something) instead of something.show
@@ -137,19 +127,100 @@ object Show extends Phase[CoreTransformed, CoreTransformed] {
137127
case Make(data, tag, targs, vargs) => Make(data, tag, targs, vargs)
138128
case PureApp(BlockVar(id, annotatedTpe, annotatedCapt), targs, vargs) if id.name.name == "myshow" =>
139129
if (targs.length != 1) sys error "expected targs to only have one argument"
140-
val targ = targs(0)
141-
if (isGroundType(targ))
142-
val bvar = getOrAddShow(targ)
143-
return Expr.PureApp(bvar, List.empty, vargs)
144-
sys error "targ was not ground type in PureApp"
130+
Expr.PureApp(ctx.getShowBlockVar(targs(0)), List.empty, vargs)
145131
case PureApp(b, targs, vargs) => PureApp(b, targs, vargs)
146132
case o => println(o); ???
147133
}
148134

149-
def generateShowInstances(declarations: List[Declaration])(using ctx: ShowContext): List[Toplevel] =
150-
ctx.defnsToGenerate.map((vt, id) => generateShowInstance(declarations, vt, id)).toList
135+
def generateShowInstancesBases(baseTypes: List[ValueType])(using ShowContext): List[Toplevel.Def] =
136+
baseTypes flatMap generateShowInstance
151137

152-
def generateShowInstance(declarations: List[Declaration], vt: ValueType, id: Id)(using ctx: ShowContext): Toplevel =
138+
// The cases here should match the list effekt.core.Type.Builtins
139+
def generateShowInstance(vt: ValueType)(using ctx: ShowContext): Option[Toplevel.Def] =
140+
ctx.backend match {
141+
case "llvm" => generateShowInstanceLLVM(vt)
142+
}
143+
144+
def generateShowInstanceLLVM(vt: ValueType)(using ctx: ShowContext): Option[Toplevel.Def] =
145+
val showId = freshShowId
146+
val paramId = Id("value")
147+
val vparam = ValueParam(paramId, vt)
148+
val valueVar = Expr.ValueVar(paramId, vt)
149+
150+
def generateBlockLit(stmt: Stmt) = BlockLit(List.empty, List.empty, List(vparam), List.empty, stmt)
151+
def generateDef(stmt: Stmt): Toplevel.Def =
152+
ctx.showDefns += (vt -> showId)
153+
val block = generateBlockLit(stmt)
154+
Toplevel.Def(showId, block)
155+
156+
vt match {
157+
case Type.TString =>
158+
// (value: String) { return value }
159+
val stmt = Stmt.Return(valueVar)
160+
Some(generateDef(stmt))
161+
case Type.TUnit =>
162+
/*
163+
(value: Unit) {
164+
"()"
165+
}
166+
*/
167+
val stmt = Stmt.Return(Expr.Literal("()", TString))
168+
Some(generateDef(stmt))
169+
case Type.TInt | Type.TChar =>
170+
/*
171+
(value: Int) {
172+
%z = call %Pos @c_bytearray_show_Int(%Int ${value})
173+
ret %Pos %z
174+
}
175+
*/
176+
val blockVar = ctx.externBlockVar("c_bytearray_show_Int")
177+
val call = Expr.PureApp(blockVar, List.empty, List(valueVar))
178+
val stmt = Stmt.Return(call)
179+
Some(generateDef(stmt))
180+
case Type.TByte =>
181+
/*
182+
(value: Byte) {
183+
%z = call %Pos @c_bytearray_show_Byte(%Byte ${value})
184+
ret %Pos %z
185+
}
186+
*/
187+
val blockVar = ctx.externBlockVar("c_bytearray_show_Byte")
188+
val call = Expr.PureApp(blockVar, List.empty, List(valueVar))
189+
val stmt = Stmt.Return(call)
190+
Some(generateDef(stmt))
191+
case Type.TDouble =>
192+
/*
193+
(value: Double) {
194+
%z = call %Pos @c_bytearray_show_Double(%Double ${value})
195+
ret %Pos %z
196+
}
197+
*/
198+
val blockVar = ctx.externBlockVar("c_bytearray_show_Double")
199+
val call = Expr.PureApp(blockVar, List.empty, List(valueVar))
200+
val stmt = Stmt.Return(call)
201+
Some(generateDef(stmt))
202+
case Type.TBoolean =>
203+
// if (value) "true" else "false"
204+
val stmt = Stmt.If(valueVar, Stmt.Return(Expr.Literal("true", TString)), Stmt.Return(Expr.Literal("false", TString)))
205+
Some(generateDef(stmt))
206+
207+
case _ => println(s"Missing case for vt: ${vt}"); None
208+
209+
}
210+
211+
def generateShowInstancesDecls(declarations: List[Declaration])(using ShowContext): List[Toplevel.Def] =
212+
declarations flatMap generateShowInstance
213+
214+
def generateShowInstance(decl: Declaration)(using ctx: ShowContext): Option[Toplevel.Def] = decl match {
215+
case dataDecl: Declaration.Data =>
216+
val freshId = freshShowId
217+
val toplevel = generateShowInstance(dataDecl, freshId)
218+
ctx.showDefns += (ValueType.Data(dataDecl.id, List.empty) -> freshId)
219+
Some(toplevel)
220+
case _ => None
221+
}
222+
223+
def generateShowInstance(declarations: List[Declaration], vt: ValueType, id: Id)(using ctx: ShowContext): Toplevel.Def =
153224
vt match {
154225
case ValueType.Data(name, targs) =>
155226
declarations.find(decl => decl.id == name).get match {
@@ -160,7 +231,7 @@ object Show extends Phase[CoreTransformed, CoreTransformed] {
160231
}
161232
???
162233

163-
def generateShowInstance(decl: Declaration.Data, id: Id)(using ShowContext): Toplevel =
234+
def generateShowInstance(decl: Declaration.Data, id: Id)(using ShowContext): Toplevel.Def =
164235
val defnId = id
165236
val valueId = Id("value")
166237
val valueTpe = ValueType.Data(decl.id, List.empty)
@@ -187,8 +258,7 @@ object Show extends Phase[CoreTransformed, CoreTransformed] {
187258
val defns = constructorStmts.zip(constructorIdVparams)
188259
val stmt = nestDefns(defns, returnVal)
189260

190-
val finalDefn = Toplevel.Def(defnId, Block.BlockLit(List.empty, List.empty, List(vparam), List.empty, stmt))
191-
finalDefn
261+
Toplevel.Def(defnId, Block.BlockLit(List.empty, List.empty, List(vparam), List.empty, stmt))
192262

193263
def nestDefns(defns: List[(Stmt, (Id, List[ValueParam]))], default: Stmt): Stmt = defns match
194264
case (head, (id, vparams)) :: rest =>
@@ -207,37 +277,29 @@ object Show extends Phase[CoreTransformed, CoreTransformed] {
207277
def constructorStmt(constr: Constructor)(using ctx: ShowContext): Stmt = constr match
208278
case Constructor(id, tparams, List()) => Return(Literal(id.name.name, TString))
209279
case Constructor(id, tparams, fields) =>
210-
val infixConcatBlockVar: Block.BlockVar = ctx.infixConcatFn
280+
val infixConcatBlockVar: Block.BlockVar = ctx.externBlockVar("infixConcat")
211281
val pureFields = fields map fieldPure
212-
val concatenated = PureApp(ctx.infixConcatFn, List.empty, List(Literal(id.name.name ++ "(", TString), concatPure(pureFields)))
282+
val concatenated = PureApp(infixConcatBlockVar, List.empty, List(Literal(id.name.name ++ "(", TString), concatPure(pureFields)))
213283
Return(concatenated)
214284

215285
// Convert a list of pure statements to comma-separated concatenated version
216286
// Literal("Just("), PureApp(show, x), Literal(", "), PureApp(show, y), Literal(")")
217287
// =>
218288
// PureApp(concat, List(Literal("Just("), PureApp(concat, List(PureApp(show, x), PureApp(concat, List(Literal(", "), ...))))
219289
def concatPure(pures: List[Expr])(using ctx: ShowContext): Expr = pures match
220-
case head :: next :: rest => PureApp(ctx.infixConcatFn, List.empty, List(head, PureApp(ctx.infixConcatFn, List.empty, List(Literal(", ", TString), concatPure(next :: rest)))))
221-
case head :: Nil => PureApp(ctx.infixConcatFn, List.empty, List(head, Literal(")", TString)))
290+
case head :: next :: rest => PureApp(ctx.externBlockVar("infixConcat"), List.empty, List(head, PureApp(ctx.externBlockVar("infixConcat"), List.empty, List(Literal(", ", TString), concatPure(next :: rest)))))
291+
case head :: Nil => PureApp(ctx.externBlockVar("infixConcat"), List.empty, List(head, Literal(")", TString)))
222292
case Nil => Literal(")", TString)
223293

224294
def fieldValueVar(field: Field): Expr = field match
225295
case Field(id, tpe) => ValueVar(id, tpe)
226296

227297
def fieldPure(field: Field)(using ctx: ShowContext): Expr.PureApp = field match
228-
case Field(id, tpe) => PureApp(ctx.showDefnsMap(List(tpe)), List.empty, List(ValueVar(id, tpe)))
229-
230-
def getOrAddShow(vt: ValueType)(using ctx: ShowContext): BlockVar = vt match
231-
case ValueType.Boxed(tpe, capt) => ???
232-
case ValueType.Data(name, targs) =>
233-
val id = ctx.defnsToGenerate.getOrElse(vt, {
234-
val freshShowId = Id("myshow")
235-
ctx.defnsToGenerate += (vt -> freshShowId)
236-
freshShowId
237-
})
238-
val freshBlockType = BlockType.Function(List.empty, List.empty, List(vt), List.empty, TString)
239-
BlockVar(id, freshBlockType, Set.empty)
240-
case ValueType.Var(name) => ???
241-
242-
def isGroundType(vt: ValueType)(using ctx: ShowContext): Boolean = ctx.groundTypes.contains(vt)
298+
case Field(id, tpe) => PureApp(ctx.getShowBlockVar(tpe), List.empty, List(ValueVar(id, tpe)))
299+
300+
var freshShowCounter = 0
301+
def freshShowId: Id =
302+
val freshId = Id("show" ++ freshShowCounter.toString())
303+
freshShowCounter += 1
304+
freshId
243305
}

0 commit comments

Comments
 (0)