/* * Copyright 2025 Casper Dalgaard Nielsen * Adam Yasser Tallouzi * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */////// This module defines and offers an interface for dealing with the types of the Datalog engine.////// A `Type` is uniquely defined by its `UnifiedTypePos` which is computed by `typeDatalog`.////// This module has a tight coupling with `Compiler` since we must assign functional predicates/// their identifiers in same order. It also has a tight coupling with `Lowering`, which assumes/// that a `Type` is the same as a `UnifiedTypePoss`.///pubmod Fixpoint3.TypeInfo {use Fixpoint3.Ast.Datalog.{BodyPredicate, BodyTerm, Datalog, Constraint, HeadPredicate, HeadTerm, VarSym}use Fixpoint3.Ast.Ram.{RamProgram, RelSym, Predicates}use Fixpoint3.Ast.Shared.PredSymuse Fixpoint3.Ast.Shared.PredSym.PredSymuse Fixpoint3.Boxeduse Fixpoint3.BoxingType.UnifiedTypePosuse Fixpoint3.Counteruse Fixpoint3.Predicate.{allFullRelSyms, getMax}use Fixpoint3.Util.getOrCrash////// As far as we are concerned a `Type` is simply a description of where/// its boxing/unboxing information is saved.///pubtypealiasType = UnifiedTypePos////// The type of a relation is simply a vector of its types.///pubtypealiasRelType = Vector[Type]////// The type of a program is a vector where position `i` stores the type of/// the relation with predicate `PredSym(_, i)`.////// For functional predicates the vector contains the types of the/// functional predicates in the same order they are met by the compiler.///pubtypealiasTypeInformation = Vector[RelType]typealiasTypeUnifier[r: Region] = MutDisjointSets[(Int64, Int32), r]typealiasVarSymUnifier[r: Region] = MutMap[String, (Int64, Int32), r]typealiasPseudoStrataOrder[r: Region] = MutMap[Int32, MutMap[Int32, List[(Int32, Constraint)], r], r]////// Cast `i` to an `Int64` and crash if `i` does not fit.///deftoInt32(i: Int64): Int32 = getOrCrash(Int64.tryToInt32(i))////// Returns the type of provenance annotations.///pubdefprovType(): Type = 0////// Returns the type information of `d`////// For functional predicates the vector returned vector contains the types of the/// functional predicates in the same order they are met by the compiler.///pubdeftypeDatalog(d: Datalog, pseudoStratums: PseudoStrataOrder[r], predicates: Predicates, withProv: Bool): TypeInformation \ r = matchd {case Datalog.Datalog(_, _) => regionrc {letidCreator = Counter.fresh(rc);// Ensure that the first index is 1.letrels = allFullRelSyms(predicates);letrelNum = getOrCrash(Int64.tryToInt32(getMax(predicates)));// The first functional will get an id 1 greater than the largest PredSym.Counter.set(relNum, idCreator);lettypeUnifier = MutDisjointSets.empty(rc);// Have to traverse in same order as `Compiler.flix`foreach ((_, pseudStrat) <- pseudoStratums) {MutMap.foldLeft(_ -> rules2 -> {List.forEach(match (_, rule) -> typeConstraint(rule, typeUnifier, idCreator), rules2) },(),pseudStrat ) };letmaxId = Counter.get(idCreator);letarr = Array.empty(rc, maxId);// Register `RelSym`s.foreach (RelSym.Symbol(PredSym(_, id), arity, _) <- rels) {Array.put(Array.repeat(rc, arity, -1), toInt32(id), arr) };// Register functional predicates.foreach (id <- Vector.range(relNum, maxId)) {letarity = computeSizeOfFunctional(Int32.toInt64(id), 0, typeUnifier);Array.put(Array.repeat(rc, arity, -1), id, arr) };lettypeCreator = Counter.fresh(rc);if (withProv) {// Reserve position 0 for provenance values.Counter.increment(typeCreator) } else {() };foreach (id <- Vector.range(0, maxId)) {letcurArr = Array.get(id, arr);foreach (index <- Vector.range(0, Array.length(curArr))) {matchMutDisjointSets.find((Int32.toInt64(id), index), typeUnifier) {case None => Array.put(Counter.getAndIncrement(typeCreator), index, curArr)case Some((id2Int64, index2)) =>letid2 = toInt32(id2Int64);letotherArr = Array.get(id2, arr);matchArray.get(index2, otherArr) {case -1 =>letnewType = Counter.getAndIncrement(typeCreator);Array.put(newType, index2, otherArr);Array.put(newType, index, curArr)casesomeType =>Array.put(someType, index, curArr) } } } };// Don't copy arrays, just cast them.unchecked_cast(arrasTypeInformation) }case_ => unreachable!() }////// Expands `typeInfo` to include the provenance annotations types.///pubdefexpandTypeInfoToProv(typeInfo: TypeInformation, predicates: Predicates): TypeInformation =letmax = getMax(predicates);Vector.mapWithIndex(i -> v -> matchInt32.toInt64(i) <max {casetrue => expandTypeToProv(v)casefalse => v },typeInfo )////// Expands `relType` to include the provenance annotations types.///pubdefexpandTypeToProv(relType: RelType): RelType = relType ++ Vector#{provType(), provType()}////// Returns the size of the output of the functional with assigned identifier `id`.///defcomputeSizeOfFunctional(id: Int64, count: Int32, typeUnifier: TypeUnifier[r]): Int32 \ r =matchMutDisjointSets.find((id, count), typeUnifier) {case None => countcase Some(_) => computeSizeOfFunctional(id, count+1, typeUnifier) }////// Returns the type of the `index`'th value of the relation with predicate `PredSym(_, id)`.///pubdefgetType(id: Int64, index: Int32, typeInfo: TypeInformation): Type = {Vector.get(index, getTypeOf(id, typeInfo)) }////// Returns the type of the relation with predicate `PredSym(_, id)`.///pubdefgetTypeOf(id: Int64, typeInfo: TypeInformation): RelType = {Vector.get(toInt32(id), typeInfo) }////// Unifies the types of `rule`.///deftypeConstraint(rule: Constraint, typeUnifier: TypeUnifier[r], counter: Counter[r]): Unit \ r = matchrule {case Constraint.Constraint(head, body) => regionrc {letvarSymUnifier = MutMap.empty(rc);typeHead(head, varSymUnifier, typeUnifier);Vector.forEach(bodyPred -> typeBodyPred(bodyPred, varSymUnifier, typeUnifier, counter), body) } }////// Unifies the types of `head`.///deftypeHead(head: HeadPredicate, varSymUnifier: VarSymUnifier[r1], typeUnifier: TypeUnifier[r2]): Unit \ r1 + r2 =let HeadPredicate.HeadAtom(PredSym(_, id), _, terms) = head;foreach ((i, t) <- ForEach.withIndex(terms)) {matcht {case HeadTerm.Var(VarSym.VarSym(sym)) =>typeVar(id, i, sym, varSymUnifier, typeUnifier)case_ => () } }////// Unifies the types of `bodyPred`.///deftypeBodyPred(bodyPred: BodyPredicate, varSymUnifier: VarSymUnifier[r1], typeUnifier: TypeUnifier[r2], counter: Counter[r2]): Unit \ r1 + r2 = matchbodyPred {case BodyPredicate.BodyAtom(PredSym(_, id), _, _, _, terms) =>Vector.forEachWithIndex(index -> term -> typeBodyTerm(id, index, term, varSymUnifier, typeUnifier), terms)case BodyPredicate.Functional(outVars, _, _inVars) =>letid = Int32.toInt64(Counter.getAndIncrement(counter));Vector.forEachWithIndex(index -> match VarSym.VarSym(sym) -> typeVar(id, index, sym, varSymUnifier, typeUnifier), outVars)case_ => () }////// Register that `(id, index) == sym`.///deftypeVar(id: Int64, index: Int32, sym: String, varSymUnifier: VarSymUnifier[r1], typeUnifier: TypeUnifier[r2]): Unit \ r1 + r2 = {let (repId, repIndex) = MutMap.getOrElsePut(sym, (id, index), varSymUnifier);if (id==repIdandrepIndex==index) {MutDisjointSets.makeSet((repId, repIndex), typeUnifier) } else {MutDisjointSets.union((repId, repIndex), (id, index), typeUnifier) } }////// Register that `(id, index) == term`.///deftypeBodyTerm(id: Int64, index: Int32, term: BodyTerm, varSymUnifier: VarSymUnifier[r1], typeUnifier: TypeUnifier[r2]): Unit \ r1 + r2 = matchterm {case BodyTerm.Wild => ()case BodyTerm.Lit(_) => ()case BodyTerm.Var(VarSym.VarSym(sym)) => typeVar(id, index, sym, varSymUnifier, typeUnifier) }}