0 | ||| Derivation interface for an end-point user
16 | ----------------------------------------
17 | --- Internal functions and instances ---
18 | ----------------------------------------
20 | --- Info of code position ---
28 | --- Collection of external `Gen`s ---
34 | --- Info for distinguishing signature checking context ---
52 | CheckResult DerivationTask = (sig : ExternalGenSignature ** exts : GenExternals ** OriginalSignatureInfo sig exts)
55 | --- Analysis functions ---
67 | -- check the given expression is a type, and normalise it
70 | -- treat the given type expression as a (possibly 0-ary) function type
73 | -----------------------------------------
74 | -- First checks in the given arguments --
75 | -----------------------------------------
77 | -- check that there are at least some parameters in the signature
79 | | [] => failAt (getFC sig) "No arguments in the generator function signature, at least a fuel argument must be present"
81 | -- check that the first argument an explicit unnamed one
83 | | _ => failAt (getFC firstArg.type) "The first argument must be explicit, unnamed, present at runtime and of type `Fuel`"
85 | -- check the type of the fuel argument
89 | ---------------------------------------------------------------
90 | -- First looks at the resulting type of a generator function --
91 | ---------------------------------------------------------------
93 | -- check the resulting type is `Gen`
94 | let IApp _ (IApp _ (IVar genFC topmostResultName) (IVar _ genEmptiness)) targetType = sigResult
95 | | _ => failAt (getFC sigResult) "The result type of the generator function must be of type \"`Gen MaybeEmpty` of desired result\""
98 | failAt (getFC sigResult) "The result type of the generator function must be of type \"`Gen MaybeEmpty` of desired result\""
101 | failAt genFC "Only `MaybeEmpty` variant of generator is supported, `\{show genEmptiness}` is given"
102 | -- this check can be changed to `==` as soon as we gen the result type normalised properly.
104 | -- treat the generated type as a dependent pair
106 | | Nothing => failAt (getFC targetType) "Unable to interpret type under `Gen` as a dependent pair"
108 | -- remember the right target type position
111 | -- treat the target type as a function application
114 | -- check out applications types
117 | -----------------------------------------
118 | -- Working with the target type family --
119 | -----------------------------------------
121 | -- acquire `TypeInfo` out of the target type `TTImp` expression
124 | -- Normal type
125 | IVar _ targetType => getInfo' targetType <|> failAt genFC "Target type `\{show targetType}` is not a top-level data definition"
127 | -- Primitive type
130 | -- Type of types
135 | -- check that target type has all unnamed arguments resolved with machine-generated names
138 | ------------------------------------------------------------
139 | -- Parse `Reflect` structures to what's needed to further --
140 | ------------------------------------------------------------
142 | -- check that all parameters of `DPair` are as expected
145 | MkArg MW ExplicitArg (Just $ MN {}) t => failAt (getFC t) "Argument of dependent pair under the resulting `Gen` seems to be repeated or badly typed"
146 | MkArg _ _ _ t => failAt (getFC t) "Argument of dependent pair under the resulting `Gen` must be named"
148 | -- check that all arguments are omega, not erased or linear; and that all arguments are properly named
149 | (givenParams, autoImplArgs, givenParamsPositions) <- map partitionEithersPos $ Prelude.for sigArgs.asVect $ \case
155 | MkArg MW ImplicitArg _ ty => failAt (getFC ty) "Implicit argument must be named and must not shadow any other name"
156 | MkArg MW ExplicitArg _ ty => failAt (getFC ty) "Explicit argument must be named and must not shadow any other name"
159 | MkArg M0 _ _ ty => failAt (getFC ty) "Erased arguments are not supported in generator function signatures"
160 | MkArg M1 _ _ ty => failAt (getFC ty) "Linear arguments are not supported in generator function signatures"
161 | MkArg MW (DefImplicit _) _ ty => failAt (getFC ty) "Default implicit arguments are not supported in generator function signatures"
163 | --------------------------------------------------
164 | -- Target type family's arguments' first checks --
165 | --------------------------------------------------
167 | -- check all the arguments of the target type are correct variable names, not complex expressions
170 | inGivenOrGenerated n = any (\(_, n', _) => n == n') givenParams || any (\(n', _) => n == n') paramsToBeGenerated
172 | err n suffix = "Name `\{show n}` is used in target's type, but is not a generated or given parameter (goes after the fuel argument); \{suffix}"
174 | IVar fc un@(UN argName) => if inGivenOrGenerated argName then pure argName else failAt fc $ err un "did you forget to add one?"
175 | IVar fc mn@(MN {}) => failAt fc $ err mn "looks like it is an implicit parameter of some underdeclared type; specify types with more precision"
176 | nonVarArg => failAt (getFC nonVarArg) "Target type's argument must be a variable name, got `\{show nonVarArg}`"
178 | -- check that all arguments names are unique
182 | -- check the given type info corresponds to the given type application, and convert a `List` to an appropriate `Vect`
184 | | No _ => fail "INTERNAL ERROR: unequal argument lists lengths: \{show targetTypeArgs.length} and \{show targetType.args.length}"
186 | ----------------------------------------------------------------------
187 | -- Check that generated and given parameter lists are actually sets --
188 | ----------------------------------------------------------------------
190 | -- check that all parameters in `parametersToBeGenerated` have different names
192 | | (_, (_, ty)) :: _ => failAt (getFC ty) "Name of the argument is not unique in the dependent pair under the resulting `Gen`"
194 | -- check that all given parameters have different names
196 | | (_, (_, _, ty)) :: _ => failAt (getFC ty) "Name of the generator function's argument is not unique"
198 | -----------------------------------------------------------------------
199 | -- Link generated and given parameters lists to the `targetTypeArgs` --
200 | -----------------------------------------------------------------------
202 | -- check that all parameters to be generated are actually used inside the target type
203 | paramsToBeGenerated <- for {b=Fin targetType.args.length} paramsToBeGenerated $ \(name, ty) => case findIndex (== name) targetTypeArgs of
207 | -- check that all target type's parameters classified as "given" are present in the given params list
208 | givenParams <- for {b=(Fin targetType.args.length, _)} givenParams $ \(explicitness, name, ty) => case findIndex (== name) targetTypeArgs of
212 | -- remember the order of given params as a permutation and forget the order of the given params, convert to a map from index to explicitness
216 | -- compute the order of generated params as a permutation
219 | -- make the resulting signature
222 | -------------------------------------
223 | -- Auto-implicit generators checks --
224 | -------------------------------------
226 | -- check that external gen does not have its own external gens
231 | -- check all auto-implicit arguments pass the checks for the `Gen` in an appropriate context
232 | autoImplArgs <- for autoImplArgs $ \tti => mapSnd (,tti) <$> checkTypeIsGen ExternalGen (assert_smaller origsig tti)
234 | -- check that all auto-imlicit arguments are unique
236 | | (_, (fc, _)) :: _ => failAt fc.targetTypeFC "Repetition of an auto-implicit external generator"
238 | -- check that the resulting generator is not in externals
240 | | Just (fc, _) => failAt fc.genFC "External generators contain the generator asked to be derived"
242 | -- forget FCs of subparsed externals
245 | ------------
246 | -- Result --
247 | ------------
253 | ++ ", \{show sigArgs.length} is not \{show genSig.givenParams.size} + \{show autoImplArgs.length}"
259 | --- Boundaries between external and internal generator functions ---
262 | nameForGen sig = let (ty, givs) = characteristics sig in UN $ Basic $ "external^<\{ty}>\{show givs}"
263 | -- I'm using `UN` but containing chars that cannot be present in the code parsed from the Idris frontend.
265 | -- this is a workaround for Idris compiler bug #2983
276 | where
278 | -- either given param or auto param
279 | mkLam : Either (Fin sig.targetType.args.length, ArgExplicitness, Name) (ExternalGenSignature, TTImp) -> TTImp -> TTImp
280 | mkLam $ Left (idx, expl, name) = lam $ MkArg MW expl.toTT .| Just (nameMod name) .| implicitTrue -- (index' sig.targetType.args idx).type
281 | -- ^^^ no type because of `nameMod` above
283 | -- TODO to think whether it's okay to calculate the name twice: here and below for a map
285 | callMainDerivedGen : DerivationClosure m => NamesInfoInTypes => ConsRecs => ExternalGenSignature -> (fuelArg : Name) -> m TTImp
289 | callGen intSig (var fuelArg) $ rewrite prf in sig.givenParams.asVect <&> \(_, _, name) => var $ nameMod name
294 | ------------------------------
295 | --- Functions for the user ---
296 | ------------------------------
298 | export
302 | let externalsSigToName = fromList $ externals.externals <&> \(sig, _) => (sig, nameForGen sig)
304 | _ <- logBounds Trace "deptycheck.derive.namesInfo" [] $ getNamesInfoInTypes signature.targetType
309 | ||| The entry-point function of automatic derivation of `Gen`'s.
310 | |||
311 | ||| Consider, you have a `data X (a : A) (b : B n) (c : C) where ...` and
312 | ||| you want a derived `Gen` for `X`.
313 | ||| Say, you want to have `a` and `c` parameters of `X` to be set by the caller and the `b` parameter to be generated.
314 | ||| For this your generator function should have a signature like `(a : A) -> (c : C) -> (n ** b : B n ** X a b c)`.
315 | ||| So, you need to define a function with this signature, say, named as `genX` and
316 | ||| to write `genX = deriveGen` as an implementation to make the body to be derived.
317 | |||
318 | ||| Say, you want `n` to be set by the caller and, as the same time, to be an implicit argument.
319 | ||| In this case, the signature of the main function to be derived,
320 | ||| becomes `{n : _} -> (a : A) -> (c : C) -> (b : B n ** X a b c)`.
321 | ||| But still, you can use this function `deriveGen` to derive a function with such signature.
322 | |||
323 | ||| Say, you want your generator to be parameterized with some external `Gen`'s.
324 | ||| Consider types `data Y where ...`, `data Z1 where ...` and `data Z2 (b : B n) where ...`.
325 | ||| For this, `auto`-parameters can be listed with `=>`-syntax in the signature.
326 | |||
327 | ||| For example, if you want to use `Gen Y` and `Gen`'s for `Z1` and `Z2` as external generators,
328 | ||| you can define your function in the following way:
329 | |||
330 | ||| ```idris
331 | ||| genX : Gen Y => Gen Z1 => ({n : _} -> {b : B n} -> Gen (Z2 b)) => {n : _} -> (a : A) -> (c : C) -> Gen (b : B n ** X a b c)
332 | ||| genX = deriveGen
333 | ||| ```
334 | |||
335 | ||| Consider also, that you may be asked for having the `Fuel` argument as the first argument in the signature
336 | ||| due to (maybe, temporary) unability of `Gen`'s to manage infinite processes of values generation.
337 | ||| So, the example from above will look like this:
338 | |||
339 | ||| ```idris
340 | ||| genX : Fuel -> (Fuel -> Gen Y) => (Fuel -> Gen Z1) => (Fuel -> {n : _} -> {b : B n} -> Gen (Z2 b)) =>
341 | ||| {n : _} -> (a : A) -> (c : C) -> Gen (b : B n ** X a b c)
342 | ||| genX = deriveGen
343 | ||| ```
344 | |||
345 | |||
350 | | Nothing => fail "The goal signature is not found. Generators derivation must be used only for fully defined signatures"
354 | ||| Alternative entry-point function of automatic derivation of `Gen`'s.
355 | |||
356 | ||| This function can be used precisely as the `deriveGen`.
357 | ||| The only difference is that this function does not rely on somewhat fragile goal mechanism
358 | ||| allowing the user to pass the desired type explicitly.
359 | |||
360 | ||| Since Idris allows simple top-level definitions to not to contain type signature,
361 | ||| one can use this derivation function as a one-liner without repetition of a desired type, e.g.
362 | |||
363 | ||| ```idris
364 | ||| genX = deriveGenFor $ Fuel -> (Fuel -> Gen Y) => (a : A) -> (c : C) -> Gen (b ** X a b c)
365 | ||| ```
373 | ||| Declares `main : IO Unit` function that prints derived generator for the given generator's signature
374 | |||
375 | ||| Caution! When `logDerivation` is set to `True`, this function would change the global logging state
376 | ||| and wouldn't turn it back.
377 | export
378 | deriveGenPrinter : {default True printTTImp : _} -> {default True logDerivation : _} -> DeriveBodyForType => (0 a : Type) -> Elab Unit
381 | when logDerivation $ declare `[%logging "deptycheck.derive.print" 5; %logging "deptycheck.derive.least-effort" 7]
387 | export
391 | putStrLn ""
392 | ]
394 | -----------------------
395 | --- Global defaults ---
396 | -----------------------