futhark 0.24.3 → 0.25.1
raw patch · 146 files changed
+9745/−7916 lines, 146 filesdep +lsp-typesdep ~freedep ~lspdep ~versionsPVP ok
version bump matches the API change (PVP)
Dependencies added: lsp-types
Dependency ranges changed: free, lsp, versions
API changes (from Hackage documentation)
- Futhark.CodeGen.ImpGen: rotateIndex :: TExp Int64 -> TExp Int64 -> TExp Int64 -> TExp Int64
- Futhark.CodeGen.ImpGen.GPU.Base: sRotateKernel :: VName -> [TExp Int64] -> VName -> CallKernelGen ()
- Futhark.IR.Mem: instance Futhark.IR.Prop.Types.FixExt ret => Futhark.IR.Prop.Types.ExtTyped (Futhark.IR.Mem.MemInfo Futhark.IR.Syntax.Core.ExtSize Futhark.IR.Syntax.Core.NoUniqueness ret)
- Futhark.IR.Mem: instance Futhark.IR.Prop.Types.Typed (Futhark.IR.Mem.MemInfo Futhark.IR.Syntax.Core.SubExp Futhark.IR.Syntax.Core.NoUniqueness ret)
- Futhark.IR.Mem.IxFun: instance Data.Foldable.Foldable Futhark.IR.Mem.IxFun.LMAD
- Futhark.IR.Mem.IxFun: instance Data.Traversable.Traversable Futhark.IR.Mem.IxFun.LMAD
- Futhark.IR.Mem.IxFun: instance Futhark.IR.Prop.Names.FreeIn num => Futhark.IR.Prop.Names.FreeIn (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Mem.IxFun: instance Futhark.IR.Prop.Names.FreeIn num => Futhark.IR.Prop.Names.FreeIn (Futhark.IR.Mem.IxFun.LMADDim num)
- Futhark.IR.Mem.IxFun: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Rename.Rename (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Mem.IxFun: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Substitute.Substitute (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Mem.IxFun: instance GHC.Base.Functor Futhark.IR.Mem.IxFun.LMAD
- Futhark.IR.Mem.IxFun: instance GHC.Classes.Eq Futhark.IR.Mem.IxFun.Monotonicity
- Futhark.IR.Mem.IxFun: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Mem.IxFun: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.IR.Mem.IxFun.LMADDim num)
- Futhark.IR.Mem.IxFun: instance GHC.Classes.Ord Futhark.IR.Mem.IxFun.Monotonicity
- Futhark.IR.Mem.IxFun: instance GHC.Classes.Ord num => GHC.Classes.Ord (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Mem.IxFun: instance GHC.Classes.Ord num => GHC.Classes.Ord (Futhark.IR.Mem.IxFun.LMADDim num)
- Futhark.IR.Mem.IxFun: instance GHC.Show.Show Futhark.IR.Mem.IxFun.Monotonicity
- Futhark.IR.Mem.IxFun: instance GHC.Show.Show num => GHC.Show.Show (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Mem.IxFun: instance GHC.Show.Show num => GHC.Show.Show (Futhark.IR.Mem.IxFun.LMADDim num)
- Futhark.IR.Mem.IxFun: instance Prettyprinter.Internal.Pretty Futhark.IR.Mem.IxFun.Monotonicity
- Futhark.IR.Mem.IxFun: instance Prettyprinter.Internal.Pretty num => Prettyprinter.Internal.Pretty (Futhark.IR.Mem.IxFun.LMAD num)
- Futhark.IR.Pretty: instance Prettyprinter.Internal.Pretty Futhark.IR.Syntax.Core.NoUniqueness
- Futhark.IR.Syntax: Copy :: VName -> BasicOp
- Futhark.IR.Syntax: Rotate :: [SubExp] -> VName -> BasicOp
- Futhark.IR.Syntax.Core: NoUniqueness :: NoUniqueness
- Futhark.IR.Syntax.Core: data NoUniqueness
- Futhark.IR.Syntax.Core: instance GHC.Base.Monoid Futhark.IR.Syntax.Core.NoUniqueness
- Futhark.IR.Syntax.Core: instance GHC.Base.Semigroup Futhark.IR.Syntax.Core.NoUniqueness
- Futhark.IR.Syntax.Core: instance GHC.Classes.Eq Futhark.IR.Syntax.Core.NoUniqueness
- Futhark.IR.Syntax.Core: instance GHC.Classes.Ord Futhark.IR.Syntax.Core.NoUniqueness
- Futhark.IR.Syntax.Core: instance GHC.Show.Show Futhark.IR.Syntax.Core.NoUniqueness
- Futhark.Internalise.Defunctionalise: instance Control.Monad.State.Class.MonadState ([Language.Futhark.ValBind], Futhark.FreshNames.VNameSource) Futhark.Internalise.Defunctionalise.DefM
- Futhark.Internalise.Defunctorise: instance Control.Monad.Writer.Class.MonadWriter (Data.DList.Internal.DList Language.Futhark.Dec) Futhark.Internalise.Defunctorise.TransformM
- Futhark.Internalise.Monad: lookupFunction' :: VName -> InternaliseM (Maybe FunInfo)
- Futhark.Internalise.Monomorphise: instance Control.Monad.Writer.Class.MonadWriter (Data.Sequence.Internal.Seq (Language.Futhark.Core.VName, Language.Futhark.ValBind)) Futhark.Internalise.Monomorphise.MonoM
- Futhark.Optimise.ArrayShortCircuiting.TopdownAnalysis: addInvAliassesVarTab :: HasMemBlock (Aliases rep) => TopdownEnv rep -> Map VName Coalesced -> VName -> Maybe (Map VName Coalesced)
- Futhark.Pkg.Types: Digits :: Word -> VUnit
- Futhark.Pkg.Types: Str :: Text -> VUnit
- Futhark.Pkg.Types: data VUnit
- Language.Futhark: type AppExp = AppExpBase Info VName
- Language.Futhark: type Case = CaseBase Info VName
- Language.Futhark: type Dec = DecBase Info VName
- Language.Futhark: type DimIndex = DimIndexBase Info VName
- Language.Futhark: type Exp = ExpBase Info VName
- Language.Futhark: type Ident = IdentBase Info VName
- Language.Futhark: type ModBind = ModBindBase Info VName
- Language.Futhark: type ModExp = ModExpBase Info VName
- Language.Futhark: type ModParam = ModParamBase Info VName
- Language.Futhark: type Pat = PatBase Info VName
- Language.Futhark: type Prog = ProgBase Info VName
- Language.Futhark: type ScalarType = ScalarTypeBase ()
- Language.Futhark: type SigBind = SigBindBase Info VName
- Language.Futhark: type SigExp = SigExpBase Info VName
- Language.Futhark: type Slice = SliceBase Info VName
- Language.Futhark: type Spec = SpecBase Info VName
- Language.Futhark: type StructTypeArg = TypeArg Size
- Language.Futhark: type TypeBind = TypeBindBase Info VName
- Language.Futhark: type TypeParam = TypeParamBase VName
- Language.Futhark: type ValBind = ValBindBase Info VName
- Language.Futhark.FreeVars: FV :: Map VName StructType -> FV
- Language.Futhark.FreeVars: [unFV] :: FV -> Map VName StructType
- Language.Futhark.FreeVars: newtype FV
- Language.Futhark.Pretty: instance (GHC.Classes.Eq vn, Language.Futhark.Pretty.IsName vn, Language.Futhark.Pretty.Annot f) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.PatBase f vn)
- Language.Futhark.Pretty: instance Language.Futhark.Pretty.IsName vn => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.IdentBase f vn)
- Language.Futhark.Pretty: instance Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.Shape dim) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.RetTypeBase dim as)
- Language.Futhark.Pretty: instance Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.Shape dim) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.ScalarTypeBase dim as)
- Language.Futhark.Pretty: instance Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.Shape dim) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.TypeBase dim as)
- Language.Futhark.Pretty: instance Prettyprinter.Internal.Pretty Language.Futhark.Syntax.Size
- Language.Futhark.Prop: NoInfo :: NoInfo a
- Language.Futhark.Prop: addAliases :: TypeBase dim asf -> (asf -> ast) -> TypeBase dim ast
- Language.Futhark.Prop: aliases :: Monoid as => TypeBase shape as -> as
- Language.Futhark.Prop: combineTypeShapes :: Monoid as => TypeBase Size as -> TypeBase Size as -> TypeBase Size as
- Language.Futhark.Prop: data NoInfo a
- Language.Futhark.Prop: foldFunTypeFromParams :: Monoid as => [PatBase Info VName] -> RetTypeBase Size as -> TypeBase Size as
- Language.Futhark.Prop: fromStruct :: TypeBase dim as -> TypeBase dim Aliasing
- Language.Futhark.Prop: setAliases :: TypeBase dim asf -> ast -> TypeBase dim ast
- Language.Futhark.Syntax: AliasBound :: VName -> Alias
- Language.Futhark.Syntax: AliasFree :: VName -> Alias
- Language.Futhark.Syntax: AnySize :: Maybe VName -> Size
- Language.Futhark.Syntax: ConstSize :: Int64 -> Size
- Language.Futhark.Syntax: NamedSize :: QualName VName -> Size
- Language.Futhark.Syntax: [aliasVar] :: Alias -> VName
- Language.Futhark.Syntax: data Alias
- Language.Futhark.Syntax: data Size
- Language.Futhark.Syntax: instance (GHC.Classes.Eq as, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.ScalarTypeBase dim as)
- Language.Futhark.Syntax: instance (GHC.Classes.Eq as, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.TypeBase dim as)
- Language.Futhark.Syntax: instance (GHC.Classes.Ord as, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.ScalarTypeBase dim as)
- Language.Futhark.Syntax: instance (GHC.Classes.Ord as, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.TypeBase dim as)
- Language.Futhark.Syntax: instance (GHC.Show.Show as, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.ScalarTypeBase dim as)
- Language.Futhark.Syntax: instance (GHC.Show.Show as, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.TypeBase dim as)
- Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.IdentBase ty vn)
- Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.PatBase f vn)
- Language.Futhark.Syntax: instance GHC.Classes.Eq (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Classes.Eq (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Classes.Eq Language.Futhark.Syntax.Alias
- Language.Futhark.Syntax: instance GHC.Classes.Eq Language.Futhark.Syntax.Size
- Language.Futhark.Syntax: instance GHC.Classes.Eq vn => GHC.Classes.Eq (Language.Futhark.Syntax.IdentBase ty vn)
- Language.Futhark.Syntax: instance GHC.Classes.Ord (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Classes.Ord (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Classes.Ord Language.Futhark.Syntax.Alias
- Language.Futhark.Syntax: instance GHC.Classes.Ord Language.Futhark.Syntax.Size
- Language.Futhark.Syntax: instance GHC.Classes.Ord vn => GHC.Classes.Ord (Language.Futhark.Syntax.IdentBase ty vn)
- Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.IdentBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Syntax: instance GHC.Show.Show Language.Futhark.Syntax.Alias
- Language.Futhark.Syntax: instance GHC.Show.Show Language.Futhark.Syntax.Size
- Language.Futhark.Syntax: instance GHC.Show.Show vn => GHC.Show.Show (Language.Futhark.Syntax.IdentBase Language.Futhark.Syntax.NoInfo vn)
- Language.Futhark.Syntax: instance GHC.Show.Show vn => GHC.Show.Show (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.NoInfo vn)
- Language.Futhark.Syntax: type Aliasing = Set Alias
- Language.Futhark.Syntax: type PatRetType = RetTypeBase Size Aliasing
- Language.Futhark.Syntax: type PatType = TypeBase Size Aliasing
- Language.Futhark.Traversals: [mapOnPatRetType] :: ASTMapper m -> PatRetType -> m PatRetType
- Language.Futhark.Traversals: [mapOnPatType] :: ASTMapper m -> PatType -> m PatType
- Language.Futhark.Traversals: [mapOnStructRetType] :: ASTMapper m -> StructRetType -> m StructRetType
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable (Language.Futhark.Syntax.IdentBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName)
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.Alias
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.Aliasing
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.PatRetType
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.PatType
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.Size
- Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.StructRetType
- Language.Futhark.TypeChecker.Monad: checkNamedSize :: MonadTypeChecker m => SrcLoc -> QualName Name -> m (QualName VName)
- Language.Futhark.TypeChecker.Terms: instance Prettyprinter.Internal.Pretty (Language.Futhark.TypeChecker.Terms.Unmatched (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName))
- Language.Futhark.TypeChecker.Terms.Monad: Global :: Locality
- Language.Futhark.TypeChecker.Terms.Monad: Local :: Locality
- Language.Futhark.TypeChecker.Terms.Monad: NameAppRes :: Maybe (QualName VName) -> SrcLoc -> NameReason
- Language.Futhark.TypeChecker.Terms.Monad: Nonlocal :: Locality
- Language.Futhark.TypeChecker.Terms.Monad: Occurrence :: Names -> Maybe Names -> SrcLoc -> Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: WasConsumed :: SrcLoc -> ValBinding
- Language.Futhark.TypeChecker.Terms.Monad: [consumed] :: Occurrence -> Maybe Names
- Language.Futhark.TypeChecker.Terms.Monad: [location] :: Occurrence -> SrcLoc
- Language.Futhark.TypeChecker.Terms.Monad: [observed] :: Occurrence -> Names
- Language.Futhark.TypeChecker.Terms.Monad: [stateDimTable] :: TermTypeState -> Map SizeSource VName
- Language.Futhark.TypeChecker.Terms.Monad: [stateNames] :: TermTypeState -> Map VName NameReason
- Language.Futhark.TypeChecker.Terms.Monad: [stateOccs] :: TermTypeState -> Occurrences
- Language.Futhark.TypeChecker.Terms.Monad: allConsumed :: Occurrences -> Names
- Language.Futhark.TypeChecker.Terms.Monad: allOccurring :: Occurrences -> Names
- Language.Futhark.TypeChecker.Terms.Monad: alternative :: TermTypeM a -> TermTypeM b -> TermTypeM (a, b)
- Language.Futhark.TypeChecker.Terms.Monad: anyConsumption :: Occurrences -> Maybe Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: badLetWithValue :: (Pretty arr, Pretty src) => arr -> src -> SrcLoc -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Monad: checkIfConsumable :: SrcLoc -> Aliasing -> TermTypeM ()
- Language.Futhark.TypeChecker.Terms.Monad: checkOccurrences :: Occurrences -> TermTypeM ()
- Language.Futhark.TypeChecker.Terms.Monad: checkTypeExpRigid :: TypeExp NoInfo Name -> RigidSource -> TermTypeM (TypeExp Info VName, StructType, [VName])
- Language.Futhark.TypeChecker.Terms.Monad: collectOccurrences :: TermTypeM a -> TermTypeM (a, Occurrences)
- Language.Futhark.TypeChecker.Terms.Monad: consume :: SrcLoc -> Aliasing -> TermTypeM ()
- Language.Futhark.TypeChecker.Terms.Monad: consuming :: Ident -> TermTypeM a -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Monad: consumption :: Aliasing -> SrcLoc -> Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: data InferredType
- Language.Futhark.TypeChecker.Terms.Monad: data Locality
- Language.Futhark.TypeChecker.Terms.Monad: data NameReason
- Language.Futhark.TypeChecker.Terms.Monad: data Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: dimFromExp :: (Exp -> SizeSource) -> Exp -> TermTypeM (Size, Maybe VName)
- Language.Futhark.TypeChecker.Terms.Monad: instance Data.Loc.Located Language.Futhark.TypeChecker.Terms.Monad.Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Terms.Monad.Locality
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Terms.Monad.Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Terms.Monad.Usage
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Classes.Ord Language.Futhark.TypeChecker.Terms.Monad.Locality
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Classes.Ord Language.Futhark.TypeChecker.Terms.Monad.Usage
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Show.Show Language.Futhark.TypeChecker.Terms.Monad.Locality
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Show.Show Language.Futhark.TypeChecker.Terms.Monad.Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Show.Show Language.Futhark.TypeChecker.Terms.Monad.Usage
- Language.Futhark.TypeChecker.Terms.Monad: liftTypeM :: TypeM a -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Monad: maybeDimFromExp :: Exp -> Maybe Size
- Language.Futhark.TypeChecker.Terms.Monad: noSizeEscape :: TermTypeM a -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Monad: noUnique :: TermTypeM a -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Monad: observation :: Aliasing -> SrcLoc -> Occurrence
- Language.Futhark.TypeChecker.Terms.Monad: observe :: Ident -> TermTypeM ()
- Language.Futhark.TypeChecker.Terms.Monad: occur :: Occurrences -> TermTypeM ()
- Language.Futhark.TypeChecker.Terms.Monad: removeSeminullOccurrences :: TermTypeM a -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Monad: returnAliased :: Name -> SrcLoc -> TermTypeM ()
- Language.Futhark.TypeChecker.Terms.Monad: seqOccurrences :: Occurrences -> Occurrences -> Occurrences
- Language.Futhark.TypeChecker.Terms.Monad: sequentially :: TermTypeM a -> (a -> Occurrences -> TermTypeM b) -> TermTypeM b
- Language.Futhark.TypeChecker.Terms.Monad: sizeFromArg :: Maybe (QualName VName) -> Exp -> TermTypeM (Size, Maybe VName)
- Language.Futhark.TypeChecker.Terms.Monad: tapOccurrences :: TermTypeM a -> TermTypeM (a, Occurrences)
- Language.Futhark.TypeChecker.Terms.Monad: type Occurrences = [Occurrence]
- Language.Futhark.TypeChecker.Terms.Monad: uniqueReturnAliased :: SrcLoc -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Pat: boundAliases :: Aliasing -> Set VName
- Language.Futhark.TypeChecker.Types: PrimSubst :: Subst t
- Language.Futhark.TypeChecker.Types: SizeSubst :: Size -> Subst t
- Language.Futhark.TypeChecker.Types: addAliasesFromType :: PatType -> PatType -> PatType
- Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.RetTypeBase Language.Futhark.Syntax.Size ())
- Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.RetTypeBase Language.Futhark.Syntax.Size Language.Futhark.Syntax.Aliasing)
- Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.TypeBase Language.Futhark.Syntax.Size ())
- Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.TypeBase Language.Futhark.Syntax.Size Language.Futhark.Syntax.Aliasing)
- Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable Language.Futhark.Pat
- Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable Language.Futhark.Syntax.Size
- Language.Futhark.TypeChecker.Types: returnType :: Aliasing -> PatType -> Diet -> PatType -> PatType
- Language.Futhark.TypeChecker.Types: subtypeOf :: TypeBase () () -> TypeBase () () -> Bool
- Language.Futhark.TypeChecker.Types: subuniqueOf :: Uniqueness -> Uniqueness -> Bool
- Language.Futhark.TypeChecker.Unify: UnknowableSize :: SrcLoc -> RigidSource -> Constraint
- Language.Futhark.TypeChecker.Unify: expect :: MonadUnify m => Usage -> StructType -> StructType -> m ()
- Language.Futhark.TypeChecker.Unify: instantiateEmptyArrayDims :: MonadUnify m => SrcLoc -> Rigidity -> RetTypeBase Size als -> m (TypeBase Size als, [VName])
- Language.Futhark.TypeChecker.Unify: normPatType :: MonadUnify m => PatType -> m PatType
+ Futhark.IR.GPUMem: instance Futhark.IR.Mem.OpReturns (Futhark.IR.GPU.Op.HostOp Futhark.IR.Rep.NoOp (Futhark.IR.Aliases.Aliases Futhark.IR.GPUMem.GPUMem))
+ Futhark.IR.MCMem: instance Futhark.IR.Mem.OpReturns (Futhark.IR.MC.Op.MCOp Futhark.IR.Rep.NoOp (Futhark.IR.Aliases.Aliases Futhark.IR.MCMem.MCMem))
+ Futhark.IR.Mem: instance Futhark.IR.Prop.Types.FixExt ret => Futhark.IR.Prop.Types.ExtTyped (Futhark.IR.Mem.MemInfo Futhark.IR.Syntax.Core.ExtSize Language.Futhark.Core.NoUniqueness ret)
+ Futhark.IR.Mem: instance Futhark.IR.Prop.Types.FixExt ret => Futhark.IR.Prop.Types.ExtTyped (Futhark.IR.Mem.MemInfo Futhark.IR.Syntax.Core.ExtSize Language.Futhark.Core.Uniqueness ret)
+ Futhark.IR.Mem: instance Futhark.IR.Prop.Types.Typed (Futhark.IR.Mem.MemInfo Futhark.IR.Syntax.Core.SubExp Language.Futhark.Core.NoUniqueness ret)
+ Futhark.IR.Mem.LMAD: Dec :: Monotonicity
+ Futhark.IR.Mem.LMAD: Inc :: Monotonicity
+ Futhark.IR.Mem.LMAD: LMAD :: num -> [LMADDim num] -> LMAD num
+ Futhark.IR.Mem.LMAD: LMADDim :: num -> num -> Int -> Monotonicity -> LMADDim num
+ Futhark.IR.Mem.LMAD: Unknown :: Monotonicity
+ Futhark.IR.Mem.LMAD: [ldMon] :: LMADDim num -> Monotonicity
+ Futhark.IR.Mem.LMAD: [ldPerm] :: LMADDim num -> Int
+ Futhark.IR.Mem.LMAD: [ldShape] :: LMADDim num -> num
+ Futhark.IR.Mem.LMAD: [ldStride] :: LMADDim num -> num
+ Futhark.IR.Mem.LMAD: [lmadDims] :: LMAD num -> [LMADDim num]
+ Futhark.IR.Mem.LMAD: [lmadOffset] :: LMAD num -> num
+ Futhark.IR.Mem.LMAD: conservativeFlatten :: LMAD (TPrimExp Int64 VName) -> Maybe (LMAD (TPrimExp Int64 VName))
+ Futhark.IR.Mem.LMAD: data LMAD num
+ Futhark.IR.Mem.LMAD: data LMADDim num
+ Futhark.IR.Mem.LMAD: data Monotonicity
+ Futhark.IR.Mem.LMAD: disjoint :: [(VName, PrimExp VName)] -> Names -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool
+ Futhark.IR.Mem.LMAD: disjoint2 :: scope -> asserts -> [(VName, PrimExp VName)] -> Names -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool
+ Futhark.IR.Mem.LMAD: disjoint3 :: Map VName Type -> [PrimExp VName] -> [(VName, PrimExp VName)] -> [PrimExp VName] -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool
+ Futhark.IR.Mem.LMAD: dynamicEqualsLMAD :: Eq num => LMAD (TPrimExp t num) -> LMAD (TPrimExp t num) -> TPrimExp Bool num
+ Futhark.IR.Mem.LMAD: instance Data.Foldable.Foldable Futhark.IR.Mem.LMAD.LMAD
+ Futhark.IR.Mem.LMAD: instance Data.Traversable.Traversable Futhark.IR.Mem.LMAD.LMAD
+ Futhark.IR.Mem.LMAD: instance Futhark.IR.Prop.Names.FreeIn num => Futhark.IR.Prop.Names.FreeIn (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: instance Futhark.IR.Prop.Names.FreeIn num => Futhark.IR.Prop.Names.FreeIn (Futhark.IR.Mem.LMAD.LMADDim num)
+ Futhark.IR.Mem.LMAD: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Rename.Rename (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: instance Futhark.Transform.Substitute.Substitute num => Futhark.Transform.Substitute.Substitute (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: instance GHC.Base.Functor Futhark.IR.Mem.LMAD.LMAD
+ Futhark.IR.Mem.LMAD: instance GHC.Classes.Eq Futhark.IR.Mem.LMAD.Monotonicity
+ Futhark.IR.Mem.LMAD: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: instance GHC.Classes.Eq num => GHC.Classes.Eq (Futhark.IR.Mem.LMAD.LMADDim num)
+ Futhark.IR.Mem.LMAD: instance GHC.Classes.Ord Futhark.IR.Mem.LMAD.Monotonicity
+ Futhark.IR.Mem.LMAD: instance GHC.Classes.Ord num => GHC.Classes.Ord (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: instance GHC.Classes.Ord num => GHC.Classes.Ord (Futhark.IR.Mem.LMAD.LMADDim num)
+ Futhark.IR.Mem.LMAD: instance GHC.Show.Show Futhark.IR.Mem.LMAD.Monotonicity
+ Futhark.IR.Mem.LMAD: instance GHC.Show.Show num => GHC.Show.Show (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: instance GHC.Show.Show num => GHC.Show.Show (Futhark.IR.Mem.LMAD.LMADDim num)
+ Futhark.IR.Mem.LMAD: instance Prettyprinter.Internal.Pretty Futhark.IR.Mem.LMAD.Monotonicity
+ Futhark.IR.Mem.LMAD: instance Prettyprinter.Internal.Pretty num => Prettyprinter.Internal.Pretty (Futhark.IR.Mem.LMAD.LMAD num)
+ Futhark.IR.Mem.LMAD: invertMonotonicity :: Monotonicity -> Monotonicity
+ Futhark.IR.Mem.LMAD: lmadPermutation :: LMAD num -> Permutation
+ Futhark.IR.Mem.LMAD: lmadShape :: LMAD num -> Shape num
+ Futhark.IR.Mem.LMAD: lmadShapeBase :: LMAD num -> Shape num
+ Futhark.IR.Mem.LMAD: makeRotIota :: IntegralExp num => Monotonicity -> num -> [num] -> LMAD num
+ Futhark.IR.Mem.LMAD: permuteFwd :: Permutation -> [a] -> [a]
+ Futhark.IR.Mem.LMAD: permuteInv :: Permutation -> [a] -> [a]
+ Futhark.IR.Mem.LMAD: substituteInLMAD :: Ord a => Map a (TPrimExp t a) -> LMAD (TPrimExp t a) -> LMAD (TPrimExp t a)
+ Futhark.IR.Mem.LMAD: type Permutation = [Int]
+ Futhark.IR.Mem.LMAD: type Shape num = [num]
+ Futhark.IR.Parse: parseDeclExtType :: FilePath -> Text -> Either Text DeclExtType
+ Futhark.IR.Parse: parseDeclType :: FilePath -> Text -> Either Text DeclType
+ Futhark.IR.Prop.Types: instance Futhark.IR.Prop.Types.ExtTyped Futhark.IR.Syntax.Core.DeclExtType
+ Futhark.IR.Syntax: RetAls :: [Int] -> [Int] -> RetAls
+ Futhark.IR.Syntax: [otherAls] :: RetAls -> [Int]
+ Futhark.IR.Syntax: [paramAls] :: RetAls -> [Int]
+ Futhark.IR.Syntax: data RetAls
+ Futhark.IR.Syntax: instance GHC.Base.Monoid Futhark.IR.Syntax.RetAls
+ Futhark.IR.Syntax: instance GHC.Base.Semigroup Futhark.IR.Syntax.RetAls
+ Futhark.IR.Syntax: instance GHC.Classes.Eq Futhark.IR.Syntax.RetAls
+ Futhark.IR.Syntax: instance GHC.Classes.Ord Futhark.IR.Syntax.RetAls
+ Futhark.IR.Syntax: instance GHC.Show.Show Futhark.IR.Syntax.RetAls
+ Futhark.IR.Syntax.Core: instance Data.Foldable.Foldable (Futhark.IR.Syntax.Core.TypeBase shape)
+ Futhark.IR.Syntax.Core: instance Data.Traversable.Traversable (Futhark.IR.Syntax.Core.TypeBase shape)
+ Futhark.Internalise.Defunctionalise: instance Control.Monad.State.Class.MonadState ([Language.Futhark.Prop.ValBind], Futhark.FreshNames.VNameSource) Futhark.Internalise.Defunctionalise.DefM
+ Futhark.Internalise.Defunctorise: instance Control.Monad.Writer.Class.MonadWriter (Data.DList.Internal.DList Language.Futhark.Prop.Dec) Futhark.Internalise.Defunctorise.TransformM
+ Futhark.Internalise.FullNormalise: instance Control.Monad.Reader.Class.MonadReader GHC.Base.String Futhark.Internalise.FullNormalise.OrderingM
+ Futhark.Internalise.FullNormalise: instance Control.Monad.State.Class.MonadState Futhark.Internalise.FullNormalise.NormState Futhark.Internalise.FullNormalise.OrderingM
+ Futhark.Internalise.FullNormalise: instance Futhark.MonadFreshNames.MonadFreshNames Futhark.Internalise.FullNormalise.OrderingM
+ Futhark.Internalise.FullNormalise: instance GHC.Base.Applicative Futhark.Internalise.FullNormalise.OrderingM
+ Futhark.Internalise.FullNormalise: instance GHC.Base.Functor Futhark.Internalise.FullNormalise.OrderingM
+ Futhark.Internalise.FullNormalise: instance GHC.Base.Monad Futhark.Internalise.FullNormalise.OrderingM
+ Futhark.Internalise.FullNormalise: transformProg :: MonadFreshNames m => [Dec] -> m [Dec]
+ Futhark.Internalise.Monomorphise: instance Control.Monad.State.Class.MonadState Futhark.Internalise.Monomorphise.ExpReplacements Futhark.Internalise.Monomorphise.MonoM
+ Futhark.Internalise.Monomorphise: instance Control.Monad.Writer.Class.MonadWriter (Data.Sequence.Internal.Seq (Language.Futhark.Core.VName, Language.Futhark.Prop.ValBind)) Futhark.Internalise.Monomorphise.MonoM
+ Futhark.Internalise.Monomorphise: instance GHC.Classes.Eq Futhark.Internalise.Monomorphise.ReplacedExp
+ Futhark.Internalise.Monomorphise: instance GHC.Show.Show Futhark.Internalise.Monomorphise.ReplacedExp
+ Futhark.Internalise.Monomorphise: instance Prettyprinter.Internal.Pretty Futhark.Internalise.Monomorphise.ReplacedExp
+ Futhark.Internalise.ReplaceRecords: instance Control.Monad.Reader.Class.MonadReader Futhark.Internalise.ReplaceRecords.Env Futhark.Internalise.ReplaceRecords.RecordM
+ Futhark.Internalise.ReplaceRecords: instance Futhark.MonadFreshNames.MonadFreshNames Futhark.Internalise.ReplaceRecords.RecordM
+ Futhark.Internalise.ReplaceRecords: instance GHC.Base.Applicative Futhark.Internalise.ReplaceRecords.RecordM
+ Futhark.Internalise.ReplaceRecords: instance GHC.Base.Functor Futhark.Internalise.ReplaceRecords.RecordM
+ Futhark.Internalise.ReplaceRecords: instance GHC.Base.Monad Futhark.Internalise.ReplaceRecords.RecordM
+ Futhark.Internalise.ReplaceRecords: transformProg :: MonadFreshNames m => [ValBind] -> m [ValBind]
+ Futhark.Internalise.TypesValues: inferAliases :: [Tree (TypeBase Shape Uniqueness)] -> [Tree (TypeBase ExtShape Uniqueness)] -> [[(TypeBase ExtShape Uniqueness, RetAls)]]
+ Futhark.Internalise.TypesValues: internaliseCoerceType :: StructType -> [TypeBase shape u] -> [TypeBase ExtShape Uniqueness]
+ Futhark.Internalise.TypesValues: internaliseConstructors :: Map Name [Tree (TypeBase ExtShape Uniqueness)] -> ([Tree (TypeBase ExtShape Uniqueness)], Map Name (Int, [Int]))
+ Futhark.Internalise.TypesValues: type Tree = Free []
+ Futhark.Optimise.ArrayShortCircuiting.DataStructs: [certs] :: CoalsEntry -> Certs
+ Futhark.Optimise.ArrayShortCircuiting.TopdownAnalysis: addInvAliasesVarTab :: HasMemBlock (Aliases rep) => TopdownEnv rep -> Map VName Coalesced -> VName -> Maybe (Map VName Coalesced)
+ Futhark.Pkg.Types: Alphanum :: Text -> Chunk
+ Futhark.Pkg.Types: Release :: NonEmpty Chunk -> Release
+ Futhark.Pkg.Types: data Chunk
+ Futhark.Pkg.Types: newtype Release
+ Futhark.Transform.Rename: renameStmsWith :: (MonadFreshNames m, Renameable rep, Rename a) => Stms rep -> a -> m (Stms rep, a)
+ Futhark.Util: chunkLike :: [[a]] -> [b] -> [[b]]
+ Futhark.Util: topologicalSort :: (a -> a -> Bool) -> [a] -> [a]
+ Futhark.Util.Pretty: prettyStringOneLine :: Pretty a => a -> String
+ Language.Futhark.Core: NoUniqueness :: NoUniqueness
+ Language.Futhark.Core: data NoUniqueness
+ Language.Futhark.Core: instance GHC.Base.Monoid Language.Futhark.Core.NoUniqueness
+ Language.Futhark.Core: instance GHC.Base.Semigroup Language.Futhark.Core.NoUniqueness
+ Language.Futhark.Core: instance GHC.Classes.Eq Language.Futhark.Core.NoUniqueness
+ Language.Futhark.Core: instance GHC.Classes.Ord Language.Futhark.Core.NoUniqueness
+ Language.Futhark.Core: instance GHC.Show.Show Language.Futhark.Core.NoUniqueness
+ Language.Futhark.Core: instance Prettyprinter.Internal.Pretty Language.Futhark.Core.NoUniqueness
+ Language.Futhark.FreeVars: data FV
+ Language.Futhark.FreeVars: fvVars :: FV -> Set VName
+ Language.Futhark.Parser: Comment :: Loc -> Text -> Comment
+ Language.Futhark.Parser: [commentLoc] :: Comment -> Loc
+ Language.Futhark.Parser: [commentText] :: Comment -> Text
+ Language.Futhark.Parser: data Comment
+ Language.Futhark.Parser: parseFutharkWithComments :: FilePath -> Text -> Either SyntaxError (UncheckedProg, [Comment])
+ Language.Futhark.Parser.Monad: Comment :: Loc -> Text -> Comment
+ Language.Futhark.Parser.Monad: [commentLoc] :: Comment -> Loc
+ Language.Futhark.Parser.Monad: [commentText] :: Comment -> Text
+ Language.Futhark.Parser.Monad: data Comment
+ Language.Futhark.Parser.Monad: instance Data.Loc.Located Language.Futhark.Parser.Monad.Comment
+ Language.Futhark.Parser.Monad: instance GHC.Classes.Eq Language.Futhark.Parser.Monad.Comment
+ Language.Futhark.Parser.Monad: instance GHC.Classes.Ord Language.Futhark.Parser.Monad.Comment
+ Language.Futhark.Parser.Monad: instance GHC.Show.Show Language.Futhark.Parser.Monad.Comment
+ Language.Futhark.Parser.Monad: parseWithComments :: ParserMonad a -> FilePath -> Text -> Either SyntaxError (a, [Comment])
+ Language.Futhark.Pretty: instance (GHC.Classes.Eq vn, Language.Futhark.Pretty.IsName vn, Language.Futhark.Pretty.Annot f, Prettyprinter.Internal.Pretty t) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.PatBase f vn t)
+ Language.Futhark.Pretty: instance (Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.Shape dim), Prettyprinter.Internal.Pretty u) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.RetTypeBase dim u)
+ Language.Futhark.Pretty: instance (Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.Shape dim), Prettyprinter.Internal.Pretty u) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Pretty: instance (Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.Shape dim), Prettyprinter.Internal.Pretty u) => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Pretty: instance forall k vn (f :: k -> *) (t :: k). Language.Futhark.Pretty.IsName vn => Prettyprinter.Internal.Pretty (Language.Futhark.Syntax.IdentBase f vn t)
+ Language.Futhark.Prop: anySize :: Size
+ Language.Futhark.Prop: arrayOfWithAliases :: u -> Shape dim -> TypeBase dim u -> TypeBase dim u
+ Language.Futhark.Prop: intrinsicVar :: Name -> VName
+ Language.Futhark.Prop: isAccType :: TypeBase d u -> Maybe (TypeBase d NoUniqueness)
+ Language.Futhark.Prop: paramToRes :: ParamType -> ResType
+ Language.Futhark.Prop: resToParam :: ResType -> ParamType
+ Language.Futhark.Prop: similarExps :: Exp -> Exp -> Maybe [(Exp, Exp)]
+ Language.Futhark.Prop: stripExp :: Exp -> Maybe Exp
+ Language.Futhark.Prop: toParam :: Diet -> TypeBase Size u -> ParamType
+ Language.Futhark.Prop: toRes :: Uniqueness -> TypeBase Size u -> ResType
+ Language.Futhark.Prop: toResRet :: Uniqueness -> RetTypeBase Size u -> ResRetType
+ Language.Futhark.Prop: type AppExp = AppExpBase Info VName
+ Language.Futhark.Prop: type Case = CaseBase Info VName
+ Language.Futhark.Prop: type Dec = DecBase Info VName
+ Language.Futhark.Prop: type DimIndex = DimIndexBase Info VName
+ Language.Futhark.Prop: type Exp = ExpBase Info VName
+ Language.Futhark.Prop: type Ident = IdentBase Info VName
+ Language.Futhark.Prop: type ModBind = ModBindBase Info VName
+ Language.Futhark.Prop: type ModExp = ModExpBase Info VName
+ Language.Futhark.Prop: type ModParam = ModParamBase Info VName
+ Language.Futhark.Prop: type Pat = PatBase Info VName
+ Language.Futhark.Prop: type Prog = ProgBase Info VName
+ Language.Futhark.Prop: type ScalarType = ScalarTypeBase ()
+ Language.Futhark.Prop: type SigBind = SigBindBase Info VName
+ Language.Futhark.Prop: type SigExp = SigExpBase Info VName
+ Language.Futhark.Prop: type Slice = SliceBase Info VName
+ Language.Futhark.Prop: type Spec = SpecBase Info VName
+ Language.Futhark.Prop: type StructTypeArg = TypeArg Size
+ Language.Futhark.Prop: type TypeBind = TypeBindBase Info VName
+ Language.Futhark.Prop: type TypeParam = TypeParamBase VName
+ Language.Futhark.Prop: type UncheckedModBind = ModBindBase NoInfo Name
+ Language.Futhark.Prop: type UncheckedSigBind = SigBindBase NoInfo Name
+ Language.Futhark.Prop: type UncheckedTypeBind = TypeBindBase NoInfo Name
+ Language.Futhark.Prop: type ValBind = ValBindBase Info VName
+ Language.Futhark.Syntax: instance (GHC.Classes.Eq u, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Eq u, GHC.Classes.Eq dim) => GHC.Classes.Eq (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Ord u, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Classes.Ord u, GHC.Classes.Ord dim) => GHC.Classes.Ord (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Show.Show (Language.Futhark.Syntax.Info t), GHC.Show.Show vn) => GHC.Show.Show (Language.Futhark.Syntax.IdentBase Language.Futhark.Syntax.NoInfo vn t)
+ Language.Futhark.Syntax: instance (GHC.Show.Show (Language.Futhark.Syntax.NoInfo t), GHC.Show.Show vn) => GHC.Show.Show (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.NoInfo vn t)
+ Language.Futhark.Syntax: instance (GHC.Show.Show u, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.ScalarTypeBase dim u)
+ Language.Futhark.Syntax: instance (GHC.Show.Show u, GHC.Show.Show dim) => GHC.Show.Show (Language.Futhark.Syntax.TypeBase dim u)
+ Language.Futhark.Syntax: instance Data.Foldable.Foldable (Language.Futhark.Syntax.RetTypeBase dim)
+ Language.Futhark.Syntax: instance Data.Foldable.Foldable (Language.Futhark.Syntax.ScalarTypeBase dim)
+ Language.Futhark.Syntax: instance Data.Foldable.Foldable (Language.Futhark.Syntax.TypeBase dim)
+ Language.Futhark.Syntax: instance Data.Loc.Located (Language.Futhark.Syntax.PatBase f vn t)
+ Language.Futhark.Syntax: instance Data.Traversable.Traversable (Language.Futhark.Syntax.RetTypeBase dim)
+ Language.Futhark.Syntax: instance Data.Traversable.Traversable (Language.Futhark.Syntax.ScalarTypeBase dim)
+ Language.Futhark.Syntax: instance Data.Traversable.Traversable (Language.Futhark.Syntax.TypeBase dim)
+ Language.Futhark.Syntax: instance Data.Traversable.Traversable f => Data.Foldable.Foldable (Language.Futhark.Syntax.PatBase f vn)
+ Language.Futhark.Syntax: instance Data.Traversable.Traversable f => Data.Traversable.Traversable (Language.Futhark.Syntax.PatBase f vn)
+ Language.Futhark.Syntax: instance Data.Traversable.Traversable f => GHC.Base.Functor (Language.Futhark.Syntax.PatBase f vn)
+ Language.Futhark.Syntax: instance GHC.Base.Monoid Language.Futhark.Syntax.Diet
+ Language.Futhark.Syntax: instance GHC.Base.Semigroup Language.Futhark.Syntax.Diet
+ Language.Futhark.Syntax: instance GHC.Classes.Eq (Language.Futhark.Syntax.Info t) => GHC.Classes.Eq (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName t)
+ Language.Futhark.Syntax: instance GHC.Classes.Eq (Language.Futhark.Syntax.NoInfo t) => GHC.Classes.Eq (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.VName t)
+ Language.Futhark.Syntax: instance GHC.Classes.Ord (Language.Futhark.Syntax.Info t) => GHC.Classes.Ord (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName t)
+ Language.Futhark.Syntax: instance GHC.Classes.Ord (Language.Futhark.Syntax.NoInfo t) => GHC.Classes.Ord (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.NoInfo Language.Futhark.Core.VName t)
+ Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.Info t) => GHC.Show.Show (Language.Futhark.Syntax.IdentBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName t)
+ Language.Futhark.Syntax: instance GHC.Show.Show (Language.Futhark.Syntax.Info t) => GHC.Show.Show (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName t)
+ Language.Futhark.Syntax: instance forall k (ty :: k -> *) vn (t :: k). Data.Loc.Located (Language.Futhark.Syntax.IdentBase ty vn t)
+ Language.Futhark.Syntax: instance forall k vn (ty :: k -> *) (t :: k). GHC.Classes.Eq vn => GHC.Classes.Eq (Language.Futhark.Syntax.IdentBase ty vn t)
+ Language.Futhark.Syntax: instance forall k vn (ty :: k -> *) (t :: k). GHC.Classes.Ord vn => GHC.Classes.Ord (Language.Futhark.Syntax.IdentBase ty vn t)
+ Language.Futhark.Syntax: sizeFromInteger :: Integer -> SrcLoc -> Size
+ Language.Futhark.Syntax: sizeFromName :: QualName VName -> SrcLoc -> Size
+ Language.Futhark.Syntax: type ParamType = TypeBase Size Diet
+ Language.Futhark.Syntax: type ResRetType = RetTypeBase Size Uniqueness
+ Language.Futhark.Syntax: type ResType = TypeBase Size Uniqueness
+ Language.Futhark.Syntax: type Size = ExpBase Info VName
+ Language.Futhark.Traversals: [mapOnParamType] :: ASTMapper m -> ParamType -> m ParamType
+ Language.Futhark.Traversals: [mapOnResRetType] :: ASTMapper m -> ResRetType -> m ResRetType
+ Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable (Language.Futhark.Syntax.IdentBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName Language.Futhark.Syntax.StructType)
+ Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName Language.Futhark.Syntax.ParamType)
+ Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable (Language.Futhark.Syntax.PatBase Language.Futhark.Syntax.Info Language.Futhark.Core.VName Language.Futhark.Syntax.StructType)
+ Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable (Language.Futhark.Syntax.TypeBase Language.Futhark.Syntax.Size Language.Futhark.Core.Uniqueness)
+ Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.ParamType
+ Language.Futhark.Traversals: instance Language.Futhark.Traversals.ASTMappable Language.Futhark.Syntax.ResRetType
+ Language.Futhark.TypeChecker.Consumption: checkValDef :: (VName, [Pat ParamType], Exp, ResRetType, Maybe (TypeExp Info VName), SrcLoc) -> ((Exp, ResRetType), [TypeError])
+ Language.Futhark.TypeChecker.Consumption: instance Control.Monad.Reader.Class.MonadReader Language.Futhark.TypeChecker.Consumption.CheckEnv Language.Futhark.TypeChecker.Consumption.CheckM
+ Language.Futhark.TypeChecker.Consumption: instance Control.Monad.State.Class.MonadState Language.Futhark.TypeChecker.Consumption.CheckState Language.Futhark.TypeChecker.Consumption.CheckM
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Base.Applicative Language.Futhark.TypeChecker.Consumption.CheckM
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Base.Functor Language.Futhark.TypeChecker.Consumption.CheckM
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Base.Functor Language.Futhark.TypeChecker.Consumption.Entry
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Base.Monad Language.Futhark.TypeChecker.Consumption.CheckM
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Consumption.Alias
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Classes.Eq a => GHC.Classes.Eq (Language.Futhark.TypeChecker.Consumption.Entry a)
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Classes.Ord Language.Futhark.TypeChecker.Consumption.Alias
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Classes.Ord a => GHC.Classes.Ord (Language.Futhark.TypeChecker.Consumption.Entry a)
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Show.Show Language.Futhark.TypeChecker.Consumption.Alias
+ Language.Futhark.TypeChecker.Consumption: instance GHC.Show.Show a => GHC.Show.Show (Language.Futhark.TypeChecker.Consumption.Entry a)
+ Language.Futhark.TypeChecker.Consumption: instance Prettyprinter.Internal.Pretty (Data.Set.Internal.Set Language.Futhark.TypeChecker.Consumption.Alias)
+ Language.Futhark.TypeChecker.Consumption: instance Prettyprinter.Internal.Pretty Language.Futhark.TypeChecker.Consumption.Alias
+ Language.Futhark.TypeChecker.Monad: checkExpForSize :: MonadTypeChecker m => UncheckedExp -> m Exp
+ Language.Futhark.TypeChecker.Monad: data TypeState
+ Language.Futhark.TypeChecker.Monad: warnings :: MonadTypeChecker m => Warnings -> m ()
+ Language.Futhark.TypeChecker.Terms: checkSizeExp :: UncheckedExp -> TypeM Exp
+ Language.Futhark.TypeChecker.Terms: instance Prettyprinter.Internal.Pretty (Language.Futhark.TypeChecker.Terms.Unmatched (Language.Futhark.Prop.Pat Language.Futhark.Syntax.StructType))
+ Language.Futhark.TypeChecker.Terms.Monad: [stateNameSource] :: TermTypeState -> VNameSource
+ Language.Futhark.TypeChecker.Terms.Monad: [stateUsed] :: TermTypeState -> Set VName
+ Language.Futhark.TypeChecker.Terms.Monad: [stateWarnings] :: TermTypeState -> Warnings
+ Language.Futhark.TypeChecker.Terms.Monad: [termChecker] :: TermEnv -> UncheckedExp -> TermTypeM Exp
+ Language.Futhark.TypeChecker.Terms.Monad: [termImportName] :: TermEnv -> ImportName
+ Language.Futhark.TypeChecker.Terms.Monad: [termOuterEnv] :: TermEnv -> Env
+ Language.Futhark.TypeChecker.Terms.Monad: data Inferred t
+ Language.Futhark.TypeChecker.Terms.Monad: instance GHC.Base.Functor Language.Futhark.TypeChecker.Terms.Monad.Inferred
+ Language.Futhark.TypeChecker.Types: ExpSubst :: Exp -> Subst t
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Prop.Pat Language.Futhark.Syntax.ParamType)
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Prop.Pat Language.Futhark.Syntax.StructType)
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.RetTypeBase Language.Futhark.Syntax.Size Language.Futhark.Core.NoUniqueness)
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.RetTypeBase Language.Futhark.Syntax.Size Language.Futhark.Core.Uniqueness)
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable (Language.Futhark.Syntax.TypeBase Language.Futhark.Syntax.Size Language.Futhark.Core.Uniqueness)
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable Language.Futhark.Prop.Exp
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable Language.Futhark.Syntax.ParamType
+ Language.Futhark.TypeChecker.Types: instance Language.Futhark.TypeChecker.Types.Substitutable Language.Futhark.Syntax.StructType
+ Language.Futhark.TypeChecker.Unify: UnknownSize :: SrcLoc -> RigidSource -> Constraint
+ Language.Futhark.TypeChecker.Unify: newFlexibleDim :: MonadUnify m => Usage -> Name -> m VName
+ Language.Futhark.TypeChecker.Unify: newRigidDim :: (MonadUnify m, Located a) => a -> RigidSource -> Name -> m VName
+ Language.Futhark.TypeChecker.Unify: normType :: MonadUnify m => StructType -> m StructType
- Futhark.CodeGen.ImpGen.GPU.Transpose: mapTransposeKernel :: String -> Integer -> TransposeArgs -> PrimType -> TransposeType -> Kernel
+ Futhark.CodeGen.ImpGen.GPU.Transpose: mapTransposeKernel :: forall int. IntExp int => (PrimType, VName -> TExp int) -> String -> Integer -> TransposeArgs int -> PrimType -> TransposeType -> Kernel
- Futhark.CodeGen.ImpGen.GPU.Transpose: type TransposeArgs = (VName, TExp Int32, VName, TExp Int32, TExp Int32, TExp Int32, TExp Int32, TExp Int32, TExp Int32, VName)
+ Futhark.CodeGen.ImpGen.GPU.Transpose: type TransposeArgs int = (VName, TExp int, VName, TExp int, TExp int, TExp int, TExp int, TExp int, TExp int, VName)
- Futhark.IR.Mem.IxFun: lmadShape :: (Eq num, IntegralExp num) => LMAD num -> Shape num
+ Futhark.IR.Mem.IxFun: lmadShape :: LMAD num -> Shape num
- Futhark.IR.RetType: class (Show rt, Eq rt, Ord rt, DeclExtTyped rt) => IsRetType rt
+ Futhark.IR.RetType: class (Show rt, Eq rt, Ord rt, ExtTyped rt, DeclExtTyped rt) => IsRetType rt
- Futhark.IR.Syntax: Apply :: Name -> [(SubExp, Diet)] -> [RetType rep] -> (Safety, SrcLoc, [SrcLoc]) -> Exp rep
+ Futhark.IR.Syntax: Apply :: Name -> [(SubExp, Diet)] -> [(RetType rep, RetAls)] -> (Safety, SrcLoc, [SrcLoc]) -> Exp rep
- Futhark.IR.Syntax: FunDef :: Maybe EntryPoint -> Attrs -> Name -> [RetType rep] -> [FParam rep] -> Body rep -> FunDef rep
+ Futhark.IR.Syntax: FunDef :: Maybe EntryPoint -> Attrs -> Name -> [(RetType rep, RetAls)] -> [FParam rep] -> Body rep -> FunDef rep
- Futhark.IR.Syntax: [funDefRetType] :: FunDef rep -> [RetType rep]
+ Futhark.IR.Syntax: [funDefRetType] :: FunDef rep -> [(RetType rep, RetAls)]
- Futhark.Internalise.Bindings: bindingFParams :: [TypeParam] -> [Pat] -> ([FParam SOACS] -> [[FParam SOACS]] -> InternaliseM a) -> InternaliseM a
+ Futhark.Internalise.Bindings: bindingFParams :: [TypeParam] -> [Pat ParamType] -> ([FParam SOACS] -> [[Tree (FParam SOACS)]] -> InternaliseM a) -> InternaliseM a
- Futhark.Internalise.Bindings: bindingLambdaParams :: [Pat] -> [Type] -> ([LParam SOACS] -> InternaliseM a) -> InternaliseM a
+ Futhark.Internalise.Bindings: bindingLambdaParams :: [Pat ParamType] -> [Type] -> ([LParam SOACS] -> InternaliseM a) -> InternaliseM a
- Futhark.Internalise.Bindings: bindingLoopParams :: [TypeParam] -> Pat -> [Type] -> ([FParam SOACS] -> [FParam SOACS] -> InternaliseM a) -> InternaliseM a
+ Futhark.Internalise.Bindings: bindingLoopParams :: [TypeParam] -> Pat ParamType -> [Type] -> ([FParam SOACS] -> [FParam SOACS] -> InternaliseM a) -> InternaliseM a
- Futhark.Internalise.Bindings: stmPat :: Pat -> [Type] -> ([VName] -> InternaliseM a) -> InternaliseM a
+ Futhark.Internalise.Bindings: stmPat :: Pat ParamType -> [Type] -> ([VName] -> InternaliseM a) -> InternaliseM a
- Futhark.Internalise.Monad: type FunInfo = ([VName], [DeclType], [FParam SOACS], [(SubExp, Type)] -> Maybe [DeclExtType])
+ Futhark.Internalise.Monad: type FunInfo = ([VName], [DeclType], [FParam SOACS], [(SubExp, Type)] -> Maybe [(DeclExtType, RetAls)])
- Futhark.Internalise.TypesValues: internaliseEntryReturnType :: StructRetType -> [[TypeBase ExtShape Uniqueness]]
+ Futhark.Internalise.TypesValues: internaliseEntryReturnType :: [Tree (TypeBase Shape Uniqueness)] -> ResRetType -> [[(TypeBase ExtShape Uniqueness, RetAls)]]
- Futhark.Internalise.TypesValues: internaliseLambdaReturnType :: TypeBase Size () -> [TypeBase shape u] -> InternaliseM [TypeBase Shape NoUniqueness]
+ Futhark.Internalise.TypesValues: internaliseLambdaReturnType :: ResType -> [TypeBase shape u] -> InternaliseM [TypeBase Shape NoUniqueness]
- Futhark.Internalise.TypesValues: internaliseLoopParamType :: TypeBase Size () -> [TypeBase shape u] -> InternaliseM [TypeBase Shape Uniqueness]
+ Futhark.Internalise.TypesValues: internaliseLoopParamType :: ParamType -> [TypeBase shape u] -> InternaliseM [TypeBase Shape Uniqueness]
- Futhark.Internalise.TypesValues: internaliseParamTypes :: [TypeBase Size ()] -> InternaliseM [[TypeBase Shape Uniqueness]]
+ Futhark.Internalise.TypesValues: internaliseParamTypes :: [ParamType] -> InternaliseM [[Tree (TypeBase Shape Uniqueness)]]
- Futhark.Internalise.TypesValues: internaliseReturnType :: StructRetType -> [TypeBase shape u] -> [TypeBase ExtShape Uniqueness]
+ Futhark.Internalise.TypesValues: internaliseReturnType :: [Tree (TypeBase Shape Uniqueness)] -> ResRetType -> [TypeBase shape u] -> [(TypeBase ExtShape Uniqueness, RetAls)]
- Futhark.Internalise.TypesValues: internaliseType :: TypeBase Size () -> [TypeBase ExtShape Uniqueness]
+ Futhark.Internalise.TypesValues: internaliseType :: TypeBase Size NoUniqueness -> [Tree (TypeBase ExtShape Uniqueness)]
- Futhark.Optimise.ArrayShortCircuiting.DataStructs: CoalsEntry :: VName -> IxFun -> Names -> Map VName Coalesced -> Map VName VName -> MemRefs -> CoalsEntry
+ Futhark.Optimise.ArrayShortCircuiting.DataStructs: CoalsEntry :: VName -> IxFun -> Names -> Map VName Coalesced -> Map VName VName -> MemRefs -> Certs -> CoalsEntry
- Futhark.Pkg.Types: SemVer :: !Word -> !Word -> !Word -> ![VChunk] -> !Maybe Text -> SemVer
+ Futhark.Pkg.Types: SemVer :: !Word -> !Word -> !Word -> !Maybe Release -> !Maybe Text -> SemVer
- Futhark.Pkg.Types: [_svPreRel] :: SemVer -> ![VChunk]
+ Futhark.Pkg.Types: [_svPreRel] :: SemVer -> !Maybe Release
- Language.Futhark.FreeVars: freeInPat :: PatBase Info VName -> Set VName
+ Language.Futhark.FreeVars: freeInPat :: Pat (TypeBase Size u) -> FV
- Language.Futhark.FreeVars: freeInType :: TypeBase Size as -> Set VName
+ Language.Futhark.FreeVars: freeInType :: TypeBase Size u -> FV
- Language.Futhark.Interpreter.Values: ValueAcc :: (Value m -> Value m -> m (Value m)) -> !Array Int (Value m) -> Value m
+ Language.Futhark.Interpreter.Values: ValueAcc :: ValueShape -> (Value m -> Value m -> m (Value m)) -> !Array Int (Value m) -> Value m
- Language.Futhark.Interpreter.Values: typeShape :: TypeBase d () -> Shape d
+ Language.Futhark.Interpreter.Values: typeShape :: TypeBase d u -> Shape d
- Language.Futhark.Parser.Lexer.Tokens: DOC :: String -> Token
+ Language.Futhark.Parser.Lexer.Tokens: DOC :: Text -> Token
- Language.Futhark.Parser.Monad: parseInMonad :: ParserMonad a -> FilePath -> Text -> ReadLineMonad (Either SyntaxError a)
+ Language.Futhark.Parser.Monad: parseInMonad :: ParserMonad a -> FilePath -> Text -> ReadLineMonad (Either SyntaxError (a, [Comment]))
- Language.Futhark.Parser.Monad: patternExp :: UncheckedPat -> ParserMonad UncheckedExp
+ Language.Futhark.Parser.Monad: patternExp :: UncheckedPat t -> ParserMonad UncheckedExp
- Language.Futhark.Prop: IntrinsicPolyFun :: [TypeParamBase VName] -> [(Diet, StructType)] -> RetTypeBase Size () -> Intrinsic
+ Language.Futhark.Prop: IntrinsicPolyFun :: [TypeParamBase VName] -> [ParamType] -> RetTypeBase Size Uniqueness -> Intrinsic
- Language.Futhark.Prop: arrayOf :: Monoid as => Uniqueness -> Shape dim -> TypeBase dim as -> TypeBase dim as
+ Language.Futhark.Prop: arrayOf :: Shape dim -> TypeBase dim NoUniqueness -> TypeBase dim NoUniqueness
- Language.Futhark.Prop: diet :: TypeBase shape as -> Diet
+ Language.Futhark.Prop: diet :: TypeBase shape Diet -> Diet
- Language.Futhark.Prop: foldFunType :: Monoid as => [(Diet, TypeBase dim pas)] -> RetTypeBase dim as -> TypeBase dim as
+ Language.Futhark.Prop: foldFunType :: [ParamType] -> ResRetType -> StructType
- Language.Futhark.Prop: funType :: [PatBase Info VName] -> StructRetType -> StructType
+ Language.Futhark.Prop: funType :: [Pat ParamType] -> ResRetType -> StructType
- Language.Futhark.Prop: patIdents :: (Functor f, Ord vn) => PatBase f vn -> Set (IdentBase f vn)
+ Language.Futhark.Prop: patIdents :: Pat t -> [Ident t]
- Language.Futhark.Prop: patNames :: (Functor f, Ord vn) => PatBase f vn -> Set vn
+ Language.Futhark.Prop: patNames :: Pat t -> [VName]
- Language.Futhark.Prop: patternMap :: Functor f => PatBase f VName -> Map VName (IdentBase f VName)
+ Language.Futhark.Prop: patternMap :: Pat t -> [(VName, t)]
- Language.Futhark.Prop: patternOrderZero :: PatBase Info vn -> Bool
+ Language.Futhark.Prop: patternOrderZero :: Pat (TypeBase d u) -> Bool
- Language.Futhark.Prop: patternParam :: PatBase Info VName -> (PName, Diet, StructType)
+ Language.Futhark.Prop: patternParam :: Pat ParamType -> (PName, Diet, StructType)
- Language.Futhark.Prop: patternStructType :: PatBase Info VName -> StructType
+ Language.Futhark.Prop: patternStructType :: Pat (TypeBase Size u) -> StructType
- Language.Futhark.Prop: patternType :: PatBase Info VName -> PatType
+ Language.Futhark.Prop: patternType :: Pat (TypeBase d u) -> TypeBase d u
- Language.Futhark.Prop: peelArray :: Int -> TypeBase dim as -> Maybe (TypeBase dim as)
+ Language.Futhark.Prop: peelArray :: Int -> TypeBase dim u -> Maybe (TypeBase dim u)
- Language.Futhark.Prop: setUniqueness :: TypeBase dim as -> Uniqueness -> TypeBase dim as
+ Language.Futhark.Prop: setUniqueness :: TypeBase dim u1 -> u2 -> TypeBase dim u2
- Language.Futhark.Prop: toStruct :: TypeBase dim as -> TypeBase dim ()
+ Language.Futhark.Prop: toStruct :: TypeBase dim u -> TypeBase dim NoUniqueness
- Language.Futhark.Prop: typeOf :: ExpBase Info VName -> PatType
+ Language.Futhark.Prop: typeOf :: ExpBase Info VName -> StructType
- Language.Futhark.Prop: typeVars :: Monoid as => TypeBase dim as -> Set VName
+ Language.Futhark.Prop: typeVars :: TypeBase dim as -> Set VName
- Language.Futhark.Prop: unfoldFunType :: TypeBase dim as -> ([(Diet, TypeBase dim ())], TypeBase dim ())
+ Language.Futhark.Prop: unfoldFunType :: TypeBase dim as -> ([TypeBase dim Diet], TypeBase dim NoUniqueness)
- Language.Futhark.Prop: unique :: TypeBase shape as -> Bool
+ Language.Futhark.Prop: unique :: TypeBase shape Uniqueness -> Bool
- Language.Futhark.Prop: uniqueness :: TypeBase shape as -> Uniqueness
+ Language.Futhark.Prop: uniqueness :: TypeBase shape Uniqueness -> Uniqueness
- Language.Futhark.Syntax: AppRes :: PatType -> [VName] -> AppRes
+ Language.Futhark.Syntax: AppRes :: StructType -> [VName] -> AppRes
- Language.Futhark.Syntax: Array :: as -> Uniqueness -> Shape dim -> ScalarTypeBase dim () -> TypeBase dim as
+ Language.Futhark.Syntax: Array :: u -> Shape dim -> ScalarTypeBase dim NoUniqueness -> TypeBase dim u
- Language.Futhark.Syntax: ArrayLit :: [ExpBase f vn] -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: ArrayLit :: [ExpBase f vn] -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Arrow :: as -> PName -> Diet -> TypeBase dim () -> RetTypeBase dim as -> ScalarTypeBase dim as
+ Language.Futhark.Syntax: Arrow :: u -> PName -> Diet -> TypeBase dim NoUniqueness -> RetTypeBase dim Uniqueness -> ScalarTypeBase dim u
- Language.Futhark.Syntax: BinOp :: (QualName vn, SrcLoc) -> f PatType -> (ExpBase f vn, f (StructType, Maybe VName)) -> (ExpBase f vn, f (StructType, Maybe VName)) -> SrcLoc -> AppExpBase f vn
+ Language.Futhark.Syntax: BinOp :: (QualName vn, SrcLoc) -> f StructType -> (ExpBase f vn, f (Maybe VName)) -> (ExpBase f vn, f (Maybe VName)) -> SrcLoc -> AppExpBase f vn
- Language.Futhark.Syntax: CasePat :: PatBase f vn -> ExpBase f vn -> SrcLoc -> CaseBase f vn
+ Language.Futhark.Syntax: CasePat :: PatBase f vn StructType -> ExpBase f vn -> SrcLoc -> CaseBase f vn
- Language.Futhark.Syntax: Coerce :: ExpBase f vn -> TypeExp f vn -> SrcLoc -> AppExpBase f vn
+ Language.Futhark.Syntax: Coerce :: ExpBase f vn -> TypeExp f vn -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Constr :: Name -> [ExpBase f vn] -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: Constr :: Name -> [ExpBase f vn] -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: DoLoop :: [VName] -> PatBase f vn -> ExpBase f vn -> LoopFormBase f vn -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
+ Language.Futhark.Syntax: DoLoop :: [VName] -> PatBase f vn ParamType -> ExpBase f vn -> LoopFormBase f vn -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
- Language.Futhark.Syntax: DocComment :: String -> SrcLoc -> DocComment
+ Language.Futhark.Syntax: DocComment :: Text -> SrcLoc -> DocComment
- Language.Futhark.Syntax: FloatLit :: Double -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: FloatLit :: Double -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: For :: IdentBase f vn -> ExpBase f vn -> LoopFormBase f vn
+ Language.Futhark.Syntax: For :: IdentBase f vn StructType -> ExpBase f vn -> LoopFormBase f vn
- Language.Futhark.Syntax: ForIn :: PatBase f vn -> ExpBase f vn -> LoopFormBase f vn
+ Language.Futhark.Syntax: ForIn :: PatBase f vn StructType -> ExpBase f vn -> LoopFormBase f vn
- Language.Futhark.Syntax: Hole :: f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: Hole :: f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Id :: vn -> f PatType -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: Id :: vn -> f t -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: Ident :: vn -> f PatType -> SrcLoc -> IdentBase f vn
+ Language.Futhark.Syntax: Ident :: vn -> f t -> SrcLoc -> IdentBase f vn t
- Language.Futhark.Syntax: IndexSection :: SliceBase f vn -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: IndexSection :: SliceBase f vn -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: IntLit :: Integer -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: IntLit :: Integer -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Lambda :: [PatBase f vn] -> ExpBase f vn -> Maybe (TypeExp f vn) -> f (Aliasing, StructRetType) -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: Lambda :: [PatBase f vn ParamType] -> ExpBase f vn -> Maybe (TypeExp f vn) -> f ResRetType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: LetFun :: vn -> ([TypeParamBase vn], [PatBase f vn], Maybe (TypeExp f vn), f StructRetType, ExpBase f vn) -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
+ Language.Futhark.Syntax: LetFun :: vn -> ([TypeParamBase vn], [PatBase f vn ParamType], Maybe (TypeExp f vn), f ResRetType, ExpBase f vn) -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
- Language.Futhark.Syntax: LetPat :: [SizeBinder vn] -> PatBase f vn -> ExpBase f vn -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
+ Language.Futhark.Syntax: LetPat :: [SizeBinder vn] -> PatBase f vn StructType -> ExpBase f vn -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
- Language.Futhark.Syntax: LetWith :: IdentBase f vn -> IdentBase f vn -> SliceBase f vn -> ExpBase f vn -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
+ Language.Futhark.Syntax: LetWith :: IdentBase f vn StructType -> IdentBase f vn StructType -> SliceBase f vn -> ExpBase f vn -> ExpBase f vn -> SrcLoc -> AppExpBase f vn
- Language.Futhark.Syntax: OpSection :: QualName vn -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: OpSection :: QualName vn -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: OpSectionLeft :: QualName vn -> f PatType -> ExpBase f vn -> (f (PName, StructType, Maybe VName), f (PName, StructType)) -> (f PatRetType, f [VName]) -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: OpSectionLeft :: QualName vn -> f StructType -> ExpBase f vn -> (f (PName, ParamType, Maybe VName), f (PName, ParamType)) -> (f ResRetType, f [VName]) -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: OpSectionRight :: QualName vn -> f PatType -> ExpBase f vn -> (f (PName, StructType), f (PName, StructType, Maybe VName)) -> f PatRetType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: OpSectionRight :: QualName vn -> f StructType -> ExpBase f vn -> (f (PName, ParamType), f (PName, ParamType, Maybe VName)) -> f ResRetType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: PatAscription :: PatBase f vn -> TypeExp f vn -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: PatAscription :: PatBase f vn t -> TypeExp f vn -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: PatAttr :: AttrInfo vn -> PatBase f vn -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: PatAttr :: AttrInfo vn -> PatBase f vn t -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: PatConstr :: Name -> f PatType -> [PatBase f vn] -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: PatConstr :: Name -> f t -> [PatBase f vn t] -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: PatLit :: PatLit -> f PatType -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: PatLit :: PatLit -> f t -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: PatParens :: PatBase f vn -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: PatParens :: PatBase f vn t -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: Prim :: PrimType -> ScalarTypeBase dim as
+ Language.Futhark.Syntax: Prim :: PrimType -> ScalarTypeBase dim u
- Language.Futhark.Syntax: Project :: Name -> ExpBase f vn -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: Project :: Name -> ExpBase f vn -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: ProjectSection :: [Name] -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: ProjectSection :: [Name] -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Record :: Map Name (TypeBase dim as) -> ScalarTypeBase dim as
+ Language.Futhark.Syntax: Record :: Map Name (TypeBase dim u) -> ScalarTypeBase dim u
- Language.Futhark.Syntax: RecordFieldImplicit :: vn -> f PatType -> SrcLoc -> FieldBase f vn
+ Language.Futhark.Syntax: RecordFieldImplicit :: vn -> f StructType -> SrcLoc -> FieldBase f vn
- Language.Futhark.Syntax: RecordPat :: [(Name, PatBase f vn)] -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: RecordPat :: [(Name, PatBase f vn t)] -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: RecordUpdate :: ExpBase f vn -> [Name] -> ExpBase f vn -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: RecordUpdate :: ExpBase f vn -> [Name] -> ExpBase f vn -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Scalar :: ScalarTypeBase dim as -> TypeBase dim as
+ Language.Futhark.Syntax: Scalar :: ScalarTypeBase dim u -> TypeBase dim u
- Language.Futhark.Syntax: Sum :: Map Name [TypeBase dim as] -> ScalarTypeBase dim as
+ Language.Futhark.Syntax: Sum :: Map Name [TypeBase dim u] -> ScalarTypeBase dim u
- Language.Futhark.Syntax: TuplePat :: [PatBase f vn] -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: TuplePat :: [PatBase f vn t] -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: TypeArgDim :: dim -> SrcLoc -> TypeArg dim
+ Language.Futhark.Syntax: TypeArgDim :: dim -> TypeArg dim
- Language.Futhark.Syntax: TypeArgType :: TypeBase dim () -> SrcLoc -> TypeArg dim
+ Language.Futhark.Syntax: TypeArgType :: TypeBase dim NoUniqueness -> TypeArg dim
- Language.Futhark.Syntax: TypeVar :: as -> Uniqueness -> QualName VName -> [TypeArg dim] -> ScalarTypeBase dim as
+ Language.Futhark.Syntax: TypeVar :: u -> QualName VName -> [TypeArg dim] -> ScalarTypeBase dim u
- Language.Futhark.Syntax: ValBind :: Maybe (f EntryPoint) -> vn -> Maybe (TypeExp f vn) -> f StructRetType -> [TypeParamBase vn] -> [PatBase f vn] -> ExpBase f vn -> Maybe DocComment -> [AttrInfo vn] -> SrcLoc -> ValBindBase f vn
+ Language.Futhark.Syntax: ValBind :: Maybe (f EntryPoint) -> vn -> Maybe (TypeExp f vn) -> f ResRetType -> [TypeParamBase vn] -> [PatBase f vn ParamType] -> ExpBase f vn -> Maybe DocComment -> [AttrInfo vn] -> SrcLoc -> ValBindBase f vn
- Language.Futhark.Syntax: Var :: QualName vn -> f PatType -> SrcLoc -> ExpBase f vn
+ Language.Futhark.Syntax: Var :: QualName vn -> f StructType -> SrcLoc -> ExpBase f vn
- Language.Futhark.Syntax: Wildcard :: f PatType -> SrcLoc -> PatBase f vn
+ Language.Futhark.Syntax: Wildcard :: f t -> SrcLoc -> PatBase f vn t
- Language.Futhark.Syntax: [appResType] :: AppRes -> PatType
+ Language.Futhark.Syntax: [appResType] :: AppRes -> StructType
- Language.Futhark.Syntax: [identName] :: IdentBase f vn -> vn
+ Language.Futhark.Syntax: [identName] :: IdentBase f vn t -> vn
- Language.Futhark.Syntax: [identSrcLoc] :: IdentBase f vn -> SrcLoc
+ Language.Futhark.Syntax: [identSrcLoc] :: IdentBase f vn t -> SrcLoc
- Language.Futhark.Syntax: [identType] :: IdentBase f vn -> f PatType
+ Language.Futhark.Syntax: [identType] :: IdentBase f vn t -> f t
- Language.Futhark.Syntax: [valBindParams] :: ValBindBase f vn -> [PatBase f vn]
+ Language.Futhark.Syntax: [valBindParams] :: ValBindBase f vn -> [PatBase f vn ParamType]
- Language.Futhark.Syntax: [valBindRetType] :: ValBindBase f vn -> f StructRetType
+ Language.Futhark.Syntax: [valBindRetType] :: ValBindBase f vn -> f ResRetType
- Language.Futhark.Syntax: data IdentBase f vn
+ Language.Futhark.Syntax: data IdentBase f vn t
- Language.Futhark.Syntax: data PatBase f vn
+ Language.Futhark.Syntax: data PatBase f vn t
- Language.Futhark.Syntax: data ScalarTypeBase dim as
+ Language.Futhark.Syntax: data ScalarTypeBase dim u
- Language.Futhark.Syntax: data TypeBase dim as
+ Language.Futhark.Syntax: data TypeBase dim u
- Language.Futhark.Syntax: type StructRetType = RetTypeBase Size ()
+ Language.Futhark.Syntax: type StructRetType = RetTypeBase Size NoUniqueness
- Language.Futhark.Syntax: type StructType = TypeBase Size ()
+ Language.Futhark.Syntax: type StructType = TypeBase Size NoUniqueness
- Language.Futhark.Syntax: type ValueType = TypeBase Int64 ()
+ Language.Futhark.Syntax: type ValueType = TypeBase Int64 NoUniqueness
- Language.Futhark.Traversals: ASTMapper :: (ExpBase Info VName -> m (ExpBase Info VName)) -> (VName -> m VName) -> (StructType -> m StructType) -> (PatType -> m PatType) -> (StructRetType -> m StructRetType) -> (PatRetType -> m PatRetType) -> ASTMapper m
+ Language.Futhark.Traversals: ASTMapper :: (ExpBase Info VName -> m (ExpBase Info VName)) -> (VName -> m VName) -> (StructType -> m StructType) -> (ParamType -> m ParamType) -> (ResRetType -> m ResRetType) -> ASTMapper m
- Language.Futhark.TypeChecker.Match: unmatched :: [Pat] -> [Match ()]
+ Language.Futhark.TypeChecker.Match: unmatched :: [Pat StructType] -> [Match ()]
- Language.Futhark.TypeChecker.Monad: lookupVar :: MonadTypeChecker m => SrcLoc -> QualName Name -> m (QualName VName, PatType)
+ Language.Futhark.TypeChecker.Monad: lookupVar :: MonadTypeChecker m => SrcLoc -> QualName Name -> m (QualName VName, StructType)
- Language.Futhark.TypeChecker.Monad: runTypeM :: Env -> ImportTable -> ImportName -> VNameSource -> TypeM a -> (Warnings, Either TypeError (a, VNameSource))
+ Language.Futhark.TypeChecker.Monad: runTypeM :: Env -> ImportTable -> ImportName -> VNameSource -> (UncheckedExp -> TypeM Exp) -> TypeM a -> (Warnings, Either TypeError (a, VNameSource))
- Language.Futhark.TypeChecker.Terms: checkFunDef :: (Name, Maybe UncheckedTypeExp, [UncheckedTypeParam], [UncheckedPat], UncheckedExp, SrcLoc) -> TypeM (VName, [TypeParam], [Pat], Maybe (TypeExp Info VName), StructRetType, Exp)
+ Language.Futhark.TypeChecker.Terms: checkFunDef :: (Name, Maybe UncheckedTypeExp, [UncheckedTypeParam], [UncheckedPat ParamType], UncheckedExp, SrcLoc) -> TypeM (VName, [TypeParam], [Pat ParamType], Maybe (TypeExp Info VName), ResRetType, Exp)
- Language.Futhark.TypeChecker.Terms.DoLoop: type CheckedLoop = ([VName], Pat, Exp, LoopFormBase Info VName, Exp)
+ Language.Futhark.TypeChecker.Terms.DoLoop: type CheckedLoop = ([VName], Pat ParamType, Exp, LoopFormBase Info VName, Exp)
- Language.Futhark.TypeChecker.Terms.DoLoop: type UncheckedLoop = (UncheckedPat, UncheckedExp, LoopFormBase NoInfo Name, UncheckedExp)
+ Language.Futhark.TypeChecker.Terms.DoLoop: type UncheckedLoop = (UncheckedPat ParamType, UncheckedExp, LoopFormBase NoInfo Name, UncheckedExp)
- Language.Futhark.TypeChecker.Terms.Monad: Ascribed :: PatType -> InferredType
+ Language.Futhark.TypeChecker.Terms.Monad: Ascribed :: t -> Inferred t
- Language.Futhark.TypeChecker.Terms.Monad: BoundV :: Locality -> [TypeParam] -> PatType -> ValBinding
+ Language.Futhark.TypeChecker.Terms.Monad: BoundV :: [TypeParam] -> StructType -> ValBinding
- Language.Futhark.TypeChecker.Terms.Monad: CheckingPat :: UncheckedPat -> InferredType -> Checking
+ Language.Futhark.TypeChecker.Terms.Monad: CheckingPat :: UncheckedPat StructType -> Inferred StructType -> Checking
- Language.Futhark.TypeChecker.Terms.Monad: CheckingReturn :: StructType -> StructType -> Checking
+ Language.Futhark.TypeChecker.Terms.Monad: CheckingReturn :: ResType -> StructType -> Checking
- Language.Futhark.TypeChecker.Terms.Monad: NoneInferred :: InferredType
+ Language.Futhark.TypeChecker.Terms.Monad: NoneInferred :: Inferred t
- Language.Futhark.TypeChecker.Terms.Monad: TermEnv :: TermScope -> Maybe Checking -> Level -> TermEnv
+ Language.Futhark.TypeChecker.Terms.Monad: TermEnv :: TermScope -> Maybe Checking -> Level -> (UncheckedExp -> TermTypeM Exp) -> Env -> ImportName -> TermEnv
- Language.Futhark.TypeChecker.Terms.Monad: TermTypeState :: Constraints -> !Int -> Map SizeSource VName -> Map VName NameReason -> Occurrences -> TermTypeState
+ Language.Futhark.TypeChecker.Terms.Monad: TermTypeState :: Constraints -> !Int -> Set VName -> Warnings -> VNameSource -> TermTypeState
- Language.Futhark.TypeChecker.Terms.Monad: allDimsFreshInType :: SrcLoc -> Rigidity -> Name -> TypeBase Size als -> TermTypeM (TypeBase Size als, Map VName Size)
+ Language.Futhark.TypeChecker.Terms.Monad: allDimsFreshInType :: Usage -> Rigidity -> Name -> TypeBase Size als -> TermTypeM (TypeBase Size als, Map VName Size)
- Language.Futhark.TypeChecker.Terms.Monad: checkTypeExpNonrigid :: TypeExp NoInfo Name -> TermTypeM (TypeExp Info VName, StructType, [VName])
+ Language.Futhark.TypeChecker.Terms.Monad: checkTypeExpNonrigid :: TypeExp NoInfo Name -> TermTypeM (TypeExp Info VName, ResType, [VName])
- Language.Futhark.TypeChecker.Terms.Monad: expType :: Exp -> TermTypeM PatType
+ Language.Futhark.TypeChecker.Terms.Monad: expType :: Exp -> TermTypeM StructType
- Language.Futhark.TypeChecker.Terms.Monad: expTypeFully :: Exp -> TermTypeM PatType
+ Language.Futhark.TypeChecker.Terms.Monad: expTypeFully :: Exp -> TermTypeM StructType
- Language.Futhark.TypeChecker.Terms.Monad: newArrayType :: SrcLoc -> Name -> Int -> TermTypeM (StructType, StructType)
+ Language.Futhark.TypeChecker.Terms.Monad: newArrayType :: Usage -> Name -> Int -> TermTypeM (StructType, StructType)
- Language.Futhark.TypeChecker.Terms.Monad: runTermTypeM :: TermTypeM a -> TypeM (a, Occurrences)
+ Language.Futhark.TypeChecker.Terms.Monad: runTermTypeM :: (UncheckedExp -> TermTypeM Exp) -> TermTypeM a -> TypeM a
- Language.Futhark.TypeChecker.Terms.Pat: binding :: Bool -> [Ident] -> TermTypeM a -> TermTypeM a
+ Language.Futhark.TypeChecker.Terms.Pat: binding :: [Ident StructType] -> TermTypeM a -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Pat: bindingIdent :: IdentBase NoInfo Name -> PatType -> (Ident -> TermTypeM a) -> TermTypeM a
+ Language.Futhark.TypeChecker.Terms.Pat: bindingIdent :: IdentBase NoInfo Name StructType -> StructType -> (Ident StructType -> TermTypeM a) -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Pat: bindingParams :: [UncheckedTypeParam] -> [UncheckedPat] -> ([TypeParam] -> [Pat] -> TermTypeM a) -> TermTypeM a
+ Language.Futhark.TypeChecker.Terms.Pat: bindingParams :: [UncheckedTypeParam] -> [UncheckedPat ParamType] -> ([TypeParam] -> [Pat ParamType] -> TermTypeM a) -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Pat: bindingPat :: [SizeBinder VName] -> PatBase NoInfo Name -> InferredType -> (Pat -> TermTypeM a) -> TermTypeM a
+ Language.Futhark.TypeChecker.Terms.Pat: bindingPat :: [SizeBinder VName] -> UncheckedPat (TypeBase Size u) -> StructType -> (Pat ParamType -> TermTypeM a) -> TermTypeM a
- Language.Futhark.TypeChecker.Terms.Pat: doNotShadow :: [String]
+ Language.Futhark.TypeChecker.Terms.Pat: doNotShadow :: [Name]
- Language.Futhark.TypeChecker.Types: checkForDuplicateNames :: MonadTypeChecker m => [UncheckedTypeParam] -> [UncheckedPat] -> m ()
+ Language.Futhark.TypeChecker.Types: checkForDuplicateNames :: MonadTypeChecker m => [UncheckedTypeParam] -> [UncheckedPat t] -> m ()
- Language.Futhark.TypeChecker.Types: checkTypeExp :: MonadTypeChecker m => TypeExp NoInfo Name -> m (TypeExp Info VName, [VName], StructRetType, Liftedness)
+ Language.Futhark.TypeChecker.Types: checkTypeExp :: MonadTypeChecker m => TypeExp NoInfo Name -> m (TypeExp Info VName, [VName], ResRetType, Liftedness)
- Language.Futhark.TypeChecker.Types: renameRetType :: MonadTypeChecker m => StructRetType -> m StructRetType
+ Language.Futhark.TypeChecker.Types: renameRetType :: MonadTypeChecker m => ResRetType -> m ResRetType
- Language.Futhark.TypeChecker.Unify: Size :: Maybe Size -> Usage -> Constraint
+ Language.Futhark.TypeChecker.Unify: Size :: Maybe Exp -> Usage -> Constraint
- Language.Futhark.TypeChecker.Unify: arrayElemType :: (MonadUnify m, Pretty (Shape dim), Monoid as) => Usage -> Text -> TypeBase dim as -> m ()
+ Language.Futhark.TypeChecker.Unify: arrayElemType :: (MonadUnify m, Pretty (Shape dim), Pretty u) => Usage -> Text -> TypeBase dim u -> m ()
- Language.Futhark.TypeChecker.Unify: dimNotes :: (Located a, MonadUnify m) => a -> Size -> m Notes
+ Language.Futhark.TypeChecker.Unify: dimNotes :: (Located a, MonadUnify m) => a -> Exp -> m Notes
- Language.Futhark.TypeChecker.Unify: equalityType :: (MonadUnify m, Pretty (Shape dim), Monoid as) => Usage -> TypeBase dim as -> m ()
+ Language.Futhark.TypeChecker.Unify: equalityType :: (MonadUnify m, Pretty (Shape dim), Pretty u) => Usage -> TypeBase dim u -> m ()
- Language.Futhark.TypeChecker.Unify: mkUsage :: SrcLoc -> Text -> Usage
+ Language.Futhark.TypeChecker.Unify: mkUsage :: Located a => a -> Text -> Usage
- Language.Futhark.TypeChecker.Unify: mkUsage' :: SrcLoc -> Usage
+ Language.Futhark.TypeChecker.Unify: mkUsage' :: Located a => a -> Usage
- Language.Futhark.TypeChecker.Unify: mustHaveField :: MonadUnify m => Usage -> Name -> PatType -> m PatType
+ Language.Futhark.TypeChecker.Unify: mustHaveField :: MonadUnify m => Usage -> Name -> StructType -> m StructType
- Language.Futhark.TypeChecker.Unify: newDimVar :: MonadUnify m => SrcLoc -> Rigidity -> Name -> m VName
+ Language.Futhark.TypeChecker.Unify: newDimVar :: MonadUnify m => Usage -> Rigidity -> Name -> m VName
- Language.Futhark.TypeChecker.Unify: unifyMostCommon :: MonadUnify m => Usage -> PatType -> PatType -> m (PatType, [VName])
+ Language.Futhark.TypeChecker.Unify: unifyMostCommon :: MonadUnify m => Usage -> StructType -> StructType -> m (StructType, [VName])
Files
- docs/c-api.rst +2/−2
- docs/error-index.rst +87/−10
- docs/glossary.rst +19/−1
- docs/language-reference.rst +65/−102
- docs/man/futhark-test.rst +15/−12
- docs/man/futhark.rst +4/−4
- futhark.cabal +11/−4
- prelude/array.fut +35/−39
- prelude/soacs.fut +5/−3
- src/Futhark/AD/Fwd.hs +0/−6
- src/Futhark/AD/Rev.hs +5/−12
- src/Futhark/AD/Rev/Hist.hs +6/−2
- src/Futhark/AD/Rev/Loop.hs +2/−1
- src/Futhark/AD/Rev/Monad.hs +8/−2
- src/Futhark/AD/Rev/Reduce.hs +8/−18
- src/Futhark/AD/Rev/Scatter.hs +3/−1
- src/Futhark/Analysis/AlgSimplify.hs +1/−1
- src/Futhark/Analysis/Metrics.hs +0/−2
- src/Futhark/Analysis/PrimExp/Convert.hs +2/−2
- src/Futhark/Analysis/SymbolTable.hs +3/−2
- src/Futhark/CLI/Dev.hs +24/−3
- src/Futhark/CLI/Doc.hs +6/−5
- src/Futhark/CLI/Eval.hs +8/−1
- src/Futhark/CLI/LSP.hs +5/−5
- src/Futhark/CLI/Literate.hs +8/−11
- src/Futhark/CLI/Main.hs +1/−1
- src/Futhark/CLI/REPL.hs +1/−1
- src/Futhark/CLI/Test.hs +28/−13
- src/Futhark/CodeGen/Backends/GenericC.hs +5/−4
- src/Futhark/CodeGen/Backends/GenericC/Types.hs +2/−2
- src/Futhark/CodeGen/Backends/GenericPython.hs +1/−1
- src/Futhark/CodeGen/Backends/PyOpenCL.hs +1/−1
- src/Futhark/CodeGen/Backends/SimpleRep.hs +2/−3
- src/Futhark/CodeGen/ImpGen.hs +3/−18
- src/Futhark/CodeGen/ImpGen/GPU.hs +88/−33
- src/Futhark/CodeGen/ImpGen/GPU/Base.hs +0/−23
- src/Futhark/CodeGen/ImpGen/GPU/Group.hs +1/−11
- src/Futhark/CodeGen/ImpGen/GPU/Transpose.hs +111/−103
- src/Futhark/Construct.hs +2/−2
- src/Futhark/Doc/Generator.hs +28/−29
- src/Futhark/IR/GPUMem.hs +4/−0
- src/Futhark/IR/MCMem.hs +5/−0
- src/Futhark/IR/Mem.hs +4/−1
- src/Futhark/IR/Mem/IxFun.hs +4/−433
- src/Futhark/IR/Mem/LMAD.hs +470/−0
- src/Futhark/IR/Mem/Simplify.hs +1/−1
- src/Futhark/IR/Parse.hs +33/−15
- src/Futhark/IR/Pretty.hs +13/−8
- src/Futhark/IR/Prop.hs +0/−1
- src/Futhark/IR/Prop/Aliases.hs +32/−40
- src/Futhark/IR/Prop/Names.hs +1/−1
- src/Futhark/IR/Prop/TypeOf.hs +1/−5
- src/Futhark/IR/Prop/Types.hs +3/−0
- src/Futhark/IR/Rephrase.hs +2/−1
- src/Futhark/IR/RetType.hs +1/−1
- src/Futhark/IR/SOACS/Simplify.hs +6/−23
- src/Futhark/IR/SegOp.hs +2/−2
- src/Futhark/IR/Syntax.hs +22/−9
- src/Futhark/IR/Syntax/Core.hs +7/−12
- src/Futhark/IR/Traversals.hs +4/−11
- src/Futhark/IR/TypeCheck.hs +70/−88
- src/Futhark/Internalise.hs +10/−4
- src/Futhark/Internalise/Bindings.hs +52/−25
- src/Futhark/Internalise/Defunctionalise.hs +288/−313
- src/Futhark/Internalise/Defunctorise.hs +6/−4
- src/Futhark/Internalise/Entry.hs +8/−3
- src/Futhark/Internalise/Exps.hs +121/−125
- src/Futhark/Internalise/FullNormalise.hs +367/−0
- src/Futhark/Internalise/LiftLambdas.hs +68/−50
- src/Futhark/Internalise/Monad.hs +3/−7
- src/Futhark/Internalise/Monomorphise.hs +650/−303
- src/Futhark/Internalise/ReplaceRecords.hs +146/−0
- src/Futhark/Internalise/TypesValues.hs +213/−58
- src/Futhark/LSP/Compile.hs +2/−2
- src/Futhark/LSP/Diagnostic.hs +46/−25
- src/Futhark/LSP/Handlers.hs +52/−42
- src/Futhark/LSP/Tool.hs +3/−3
- src/Futhark/Optimise/ArrayShortCircuiting.hs +7/−2
- src/Futhark/Optimise/ArrayShortCircuiting/ArrayCoalescing.hs +180/−160
- src/Futhark/Optimise/ArrayShortCircuiting/DataStructs.hs +17/−13
- src/Futhark/Optimise/ArrayShortCircuiting/MemRefAggreg.hs +9/−2
- src/Futhark/Optimise/ArrayShortCircuiting/TopdownAnalysis.hs +14/−24
- src/Futhark/Optimise/CSE.hs +19/−13
- src/Futhark/Optimise/DoubleBuffer.hs +9/−6
- src/Futhark/Optimise/Fusion.hs +1/−1
- src/Futhark/Optimise/GenRedOpt.hs +0/−1
- src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs +2/−2
- src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs +3/−16
- src/Futhark/Optimise/ReduceDeviceSyncs/MigrationTable.hs +4/−16
- src/Futhark/Optimise/Simplify/Engine.hs +17/−4
- src/Futhark/Optimise/Simplify/Rep.hs +1/−1
- src/Futhark/Optimise/Simplify/Rules.hs +11/−11
- src/Futhark/Optimise/Simplify/Rules/BasicOp.hs +14/−56
- src/Futhark/Optimise/Simplify/Rules/ClosedForm.hs +1/−1
- src/Futhark/Optimise/Simplify/Rules/Index.hs +3/−18
- src/Futhark/Optimise/Simplify/Rules/Loop.hs +1/−1
- src/Futhark/Optimise/Simplify/Rules/Simple.hs +7/−7
- src/Futhark/Optimise/TileLoops.hs +13/−18
- src/Futhark/Pass.hs +1/−1
- src/Futhark/Pass/AD.hs +2/−2
- src/Futhark/Pass/ExpandAllocations.hs +1/−1
- src/Futhark/Pass/ExplicitAllocations.hs +22/−5
- src/Futhark/Pass/ExtractKernels/DistributeNests.hs +10/−11
- src/Futhark/Pass/ExtractKernels/Interchange.hs +4/−1
- src/Futhark/Pass/ExtractKernels/Intragroup.hs +1/−1
- src/Futhark/Pass/KernelBabysitting.hs +7/−22
- src/Futhark/Pass/LiftAllocations.hs +74/−83
- src/Futhark/Pass/LowerAllocations.hs +17/−33
- src/Futhark/Pkg/Types.hs +5/−5
- src/Futhark/Test.hs +2/−2
- src/Futhark/Transform/FirstOrderTransform.hs +5/−6
- src/Futhark/Transform/Rename.hs +14/−1
- src/Futhark/Util.hs +38/−1
- src/Futhark/Util/Pretty.hs +9/−4
- src/Language/Futhark.hs +0/−80
- src/Language/Futhark/Core.hs +15/−0
- src/Language/Futhark/FreeVars.hs +42/−55
- src/Language/Futhark/Interpreter.hs +244/−181
- src/Language/Futhark/Interpreter/Values.hs +12/−8
- src/Language/Futhark/Parser.hs +13/−2
- src/Language/Futhark/Parser/Lexer.x +1/−1
- src/Language/Futhark/Parser/Lexer/Tokens.hs +1/−1
- src/Language/Futhark/Parser/Monad.hs +33/−7
- src/Language/Futhark/Parser/Parser.y +23/−21
- src/Language/Futhark/Pretty.hs +36/−28
- src/Language/Futhark/Primitive.hs +3/−0
- src/Language/Futhark/Prop.hs +1516/−1397
- src/Language/Futhark/Query.hs +5/−7
- src/Language/Futhark/Syntax.hs +162/−130
- src/Language/Futhark/Traversals.hs +97/−94
- src/Language/Futhark/TypeChecker.hs +28/−27
- src/Language/Futhark/TypeChecker/Consumption.hs +972/−0
- src/Language/Futhark/TypeChecker/Match.hs +2/−2
- src/Language/Futhark/TypeChecker/Modules.hs +41/−46
- src/Language/Futhark/TypeChecker/Monad.hs +28/−31
- src/Language/Futhark/TypeChecker/Terms.hs +1704/−1700
- src/Language/Futhark/TypeChecker/Terms/DoLoop.hs +179/−325
- src/Language/Futhark/TypeChecker/Terms/Monad.hs +145/−477
- src/Language/Futhark/TypeChecker/Terms/Pat.hs +87/−171
- src/Language/Futhark/TypeChecker/Types.hs +98/−253
- src/Language/Futhark/TypeChecker/Unify.hs +145/−173
- unittests/Futhark/IR/SyntaxTests.hs +19/−2
- unittests/Futhark/Internalise/TypesValuesTests.hs +173/−0
- unittests/Language/Futhark/SyntaxTests.hs +43/−29
- unittests/Language/Futhark/TypeChecker/TypesTests.hs +5/−4
- unittests/futhark_tests.hs +2/−0
docs/c-api.rst view
@@ -293,7 +293,7 @@ tuples, etc) is represented by an opaque C struct named ``futhark_opaque_foo``. In the general case, ``foo`` will be a hash of the internal representation. However, if you insert an explicit-type annotations in the entry point (and the type name contains only+type annotation in the entry point (and the type name contains only characters valid in C identifiers), that name will be used. Note that arrays contain brackets, which are not valid in identifiers. Defining a type abbreviation is the best way around this.@@ -645,7 +645,7 @@ * For opaques that are actually records (including tuples): * The list of fields, including their type and a projection- function. The field ordering here is the one used expected by+ function. The field ordering here is the one expected by the *new* function. * The name of the C *new* function for creating a record from
docs/error-index.rst view
@@ -267,6 +267,44 @@ def f () = copy x +.. _size-expression-bind:++"Size expression with binding is replaced by unknown size."+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++To illustrate this error, consider the following program++.. code-block:: futhark++ def main (xs: *[]i64) =+ let a = iota (let n = 10 in n+n)+ in ...++Intuitively, the type of ``a`` should be ``[let n = 10 in n+n]i32``,+but this puts a binding into a size expression, which is invalid.+Therefore, the type checker invents an :term:`unknown size`+variable, say ``l``, and assigns ``a`` the type ``[l]i32``.++.. _size-expression-consume:++"Size expression with consumption is replaced by unknown size."+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++To illustrate this error, consider the following program++.. code-block:: futhark++ def consume (xs: *[]i64): i64 = xs[0]++ def main (xs: *[]i64) =+ let a = iota (consume xs)+ in ...++Intuitively, the type of ``a`` should be ``[consume ys]i32``, but this+puts a consumption of the array ``ys`` into a size expression, which+is invalid. Therefore, the type checker invents an :term:`unknown+size` variable, say ``l``, and assigns ``a`` the type ``[l]i32``.+ .. _inaccessible-size: "Parameter *x* refers to size *y* which will not be accessible to the caller@@ -378,14 +416,14 @@ In many other cases, we can lift out the "size-producing" expressions into a separate ``let``-binding preceding the problematic expressions. -.. _unknowable-param-def:+.. _unknown-param-def: -"Unknowable size *x* in parameter of *y*"-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+"Unknown size *x* in parameter of *y*"+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This error occurs when you define a function that can never be applied, as it requires an input of a specific size, and that size is-not known. Somewhat contrived example:+an :term:`unknown size`. Somewhat contrived example: .. code-block:: futhark @@ -471,8 +509,9 @@ "Existential size *n* not used as array size" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This error occurs for type expressions that use explicit existential-quantification in an incorrect way, such as the following examples:+This error occurs for type expressions that bind an existential size+for which there is no :term:`constructive use`, such as in the+following examples: .. code-block:: futhark @@ -481,12 +520,12 @@ ?[n].bool -> [n]bool When we use existential quantification, we are required to use the-size within its scope, *and* it must not exclusively be used to the-right of function arrow.+size constructively within its scope, *in particular* it must not be+exclusively as the parameter or return type of a function. To understand the motivation behind this rule, consider that when we-use an existential quantifier we are saying that there is *some size*,-it just cannot be known statically, but must be read from some value+use an existential quantifier we are saying that there is *some size*.+The size is not known statically, but must be read from some value (i.e. array) at runtime. In the first example above, the existential size ``n`` is not used at all, so the actual value cannot be determined at runtime. In the second example, while an array@@ -943,3 +982,41 @@ .. code-block:: futhark entry f (n: i64) : [0][n]i32 = []++.. _nonconstructive-entry:++"Entry point size parameter [n] only used non-constructively."+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++This error occurs for programs such as the following::++.. code-block:: futhark++ entry main [x] (A: [x+1]i32) = ...++The size parameter ``[x]`` is only used in an size expression ``x+1``,+rather than directly as an array size. This is allowed for ordinary+functions, but not for entry points. The reason is that entry points+are not subject to ordinary type inference, as they are called from+the external world, meaning that the value of the size parameter+``[x]`` will have to be determined from the size of the array ``A``.+This is in principle not a problem for simple sizes like ``x+1``, as+it is obvious that ``x == length A - 1``, but in the general case it+would require computing function inverses that might not exist. For+this reason, entry points require that all size parameters are used+:term:`constructively<constructive use>`.++As a workaround, you can rewrite the entry point as follows:++.. code-block:: futhark++ entry main [n] (A: [n]i32) =+ let x = n-1+ let A = A :> [x+1]i32+ ...++Or by passing the ``x`` explicitly:++.. code-block:: futhark++ entry main (x: i64) (A: [x+1]i32) = ...
docs/glossary.rst view
@@ -73,11 +73,13 @@ * ``[n]bool`` * ``([n]bool, bool -> [n]bool)``+ * ``([n]bool, [n+1]bool)`` The following do not: * ``[n+1]bool`` * ``bool -> [n]bool``+ * ``[n]bool -> bool`` Consumption @@ -296,6 +298,20 @@ The symbolic size of an array dimension or :term:`abstract type`. + Size expression++ An expression that occurs as the size of an array or size+ argument. For example, in the type ``[x+2]i32``, ``x+2`` is a+ size expression. Size expressions can occur syntactically in+ source code, or due to parameter substitution when applying a+ :term:`size-dependent function`.++ Size-dependent function++ A function where the size of the result depends on the values of+ the parameters. The function ``iota`` is perhaps the simplest+ example.+ Size types Size-dependent types @@ -385,7 +401,9 @@ Unknown size A size produced by invoking a function whose result type contains- an existentially quantified size, such as ``filter``.+ an existentially quantified size, such as ``filter``, or because+ the original :term:`size expression` involves variables that have+ gone out of scope. Value
docs/language-reference.rst view
@@ -117,9 +117,8 @@ Compound types can be constructed based on the primitive types. The Futhark type system is entirely structural, and type abbreviations are-merely shorthands (with one exception, see-:ref:`sizes-in-abbreviations`). The only exception is abstract types-whose definition has been hidden via the module system (see+merely shorthands. The only exception is abstract types whose+definition has been hidden via the module system (see :ref:`module-system`). .. productionlist::@@ -136,18 +135,17 @@ and are of type ``()``. .. productionlist::- array_type: "[" [`dim`] "]" `type`- dim: `qualid` | `decimal`+ array_type: "[" [`exp`] "]" `type` An array value is written as a sequence of zero or more comma-separated values enclosed in square brackets: ``[1,2,3]``. An array type is written as ``[d]t``, where ``t`` is the element type of-the array, and ``d`` is an integer or variable indicating the size.-We can often elide ``d`` and write just ``[]`` (an *anonymous size*),-in which case the size will be inferred. An anonymous size is a-syntactic shorthand, and is always replaced by an actual size by the-type checker (either via inference or by inventing a new name,-depending on context).+the array, and ``d`` is an expression of type ``i64`` indicating the+number of elements in the array. We can elide ``d`` and write just+``[]`` (an :term:`anonymous size`), in which case the size will be+inferred. An anonymous size is a syntactic shorthand, and is always+replaced by an actual size by the type checker (either via inference+or by inventing a new name, depending on context). As an example, an array of three integers could be written as ``[1,2,3]``, and has type ``[3]i32``. An empty array is written as@@ -220,7 +218,7 @@ An existential size quantifier brings an unknown size into scope within a type. This can be used to encode constraints for statically-unknowable array sizes.+unknown array sizes. Declarations ------------@@ -268,10 +266,9 @@ Hindley-Milner-style type inference is supported. A parameter may be given a type with the notation ``(name: type)``. Functions may not be-recursive. You may put *size annotations* in the return type and-parameter types; see `Size Types`_. A function can be *polymorphic*-by using type parameters, in the same way as for `Type-Abbreviations`_::+recursive. The sizes of the arguments can be constrained - see `Size+Types`_. A function can be *polymorphic* by using type parameters, in+the same way as for `Type Abbreviations`_:: def reverse [n] 't (xs: [n]t): [n]t = xs[::-1] @@ -405,9 +402,9 @@ def x: two_intvecs [2] = (iota 2, replicate 2 0) -Size parameters work much like shape declarations for arrays. Like-shape declarations, they can be elided via square brackets containing-nothing. All size parameters must be used in the definition of the+When referencing a type abbreviation, size parameters work much like+array sizes. Like sizes, they can be passed an anonymous size+(``[]``). All size parameters must be used in the definition of the type abbreviation. A type parameter prefixed with a single quote is a *type parameter*.@@ -448,7 +445,6 @@ : | "(" `exp` ")" "[" `index` ("," `index`)* "]" : | `quals` "." "(" `exp` ")" : | "[" `exp` ("," `exp`)* "]"- : | "[" `exp` [".." `exp`] "..." `exp` "]" : | "(" `qualbinop` ")" : | "(" `exp` `qualbinop` ")" : | "(" `qualbinop` `exp` ")"@@ -579,6 +575,7 @@ left ``|>`` right ``<|`` right ``->``+ left ``**`` left juxtaposition ================= ============= @@ -939,6 +936,10 @@ let-generalised, meaning it has a monomorphic type. This can be significant if ``e`` is of functional type. +If ``e`` is of type ``i64`` and ``pat`` binds only a single name+``v``, then the type of the overall expression is the type of+``body``, but with any occurence of ``v`` replaced by ``e``.+ ``let [n] pat = e in body`` ........................... @@ -1092,34 +1093,34 @@ Size Types ---------- -Futhark supports a simple system of size-dependent types that-statically verifies that the sizes of arrays passed to a function are-compatible. The focus is on simplicity, not completeness.+Futhark supports a system of size-dependent types that statically+checks that the sizes of arrays passed to a function are compatible. Whenever a pattern occurs (in ``let``, ``loop``, and function-parameters), as well as in return types, *size annotations* may be-used to express invariants about the shapes of arrays that are-accepted or produced by the function. For example::+parameters), as well as in return types, the types of the bindings+express invariants about the shapes of arrays that are accepted or+produced by the function. For example:: def f [n] (a: [n]i32) (b: [n]i32): [n]i32 = map2 (+) a b -We use a *size parameter*, ``[n]``, to explicitly quantify sizes. The-``[n]`` parameter is not explicitly passed when calling ``f``.+We use a *size parameter*, ``[n]``, to explicitly quantify a size.+The ``[n]`` parameter is not explicitly passed when calling ``f``. Rather, its value is implicitly deduced from the arguments passed for-the value parameters. An array type can contain *anonymous-dimensions*, e.g. ``[]i32``, for which the type checker will invent-fresh size parameters, which ensures that all arrays have a (symbolic)-size. On the right-hand side of a function arrow ("return types"),-this results in an *existential size* that is not known until the-function is fully applied, e.g::+the value parameters. An array type can contain *anonymous sizes*,+e.g. ``[]i32``, for which the type checker will invent fresh size+parameters, which ensures that all arrays have a size. On the+right-hand side of a function arrow ("return types"), this results in+an *existential size* that is not known until the function is fully+applied, e.g:: val filter [n] 'a : (p: a -> bool) -> (as: [n]a) -> ?[k].[k]a -A size annotation can also be an integer constant (with no suffix).-Size parameters can be used as ordinary variables within the scope of-the parameters. The type checker verifies that the program obeys any-constraints imposed by size annotations.+Sizes can be any expression of type ``i64`` that does not consume any+free variables. Size parameters can be used as ordinary variables of+type ``i64`` within the scope of the parameters. The type checker+verifies that the program obeys any constraints imposed by size+annotations. *Size-dependent types* are supported, as the names of parameters can be used in the return type of a function::@@ -1128,30 +1129,29 @@ An application ``replicate 10 0`` will have type ``[10]i32``. -Whenever we write a type ``[n]t``, ``n`` must already be a variable of-type ``i64`` in scope (possibly by being bound as a size parameter).+Whenever we write a type ``[e]t``, ``e`` must be a well-typed+expression of type ``i64`` in scope (possibly by referencing names+bound as a size parameter). .. _unknown-sizes: Unknown sizes ~~~~~~~~~~~~~ -Since sizes must be constants or variables, there are many cases where-the type checker cannot assign a precise size to the result of some-operation. For example, the type of ``concat`` should conceptually be::-- val concat [n] [m] 't : [n]t -> [m]t -> [n+m]t+There are cases where the type checker cannot assign a precise size to+the result of some operation. For example, the type of ``filter``+is:: -But this is not presently allowed. Instead, the return type contains-an existential size::+ val filter [n] 'a : (a -> bool) -> [n]t -> ?[m].[m]t - val concat [n] [m] 't : [n]t -> [m]t -> ?[k].[k]t+The function returns of an array of *some existential size* ``m``, but+it cannot be known in advance. -When an application ``concat xs ys`` is found, the result will be of-type ``[k']t``, where ``k'`` is a fresh *unknown* size variable that-is considered distinct from every other size in the program. It is-often necessary to perform a size coercion (see `Size coercion`_) to-convert an unknown size to a known size.+When an application ``filter p xs`` is found, the result will be of+type ``[k]t``, where ``k`` is a fresh *unknown size* that is+considered distinct from every other size in the program. It is+sometimes necessary to perform a size coercion (see `Size coercion`_)+to convert an unknown size to a known size. Generally, unknown sizes are constructed whenever the true size cannot be expressed. The following lists all possible sources of unknown@@ -1160,22 +1160,22 @@ Size going out of scope ....................... -An unknown size is created when the proper size of an array refers to-a name that has gone out of scope::+An unknown size is created in some cases when the a type references a+name that has gone out of scope:: - let c = a + b- in replicate c 0+ match ...+ case #some c -> replicate c 0 The type of ``replicate c 0`` is ``[c]i32``, but since ``c`` is locally bound, the type of the entire expression is ``[k]i32`` for some fresh ``k``. -Compound expression passed as function argument-...............................................+Consuming expression passed as function argument+................................................ -Intuitively, the type of ``replicate (x+y) 0`` should be ``[x+y]i32``,-but since sizes must be names or constants, this is not expressible.-Therefore an unknown size variable is created and the size of the+The type of ``replicate e 0`` should be ``[e]i32``, but if ``e`` is an+expression that is not valid as a size, this is not expressible.+Therefore an unknown size ``k`` is created and the size of the expression becomes ``[k]i32``. Compound expression used as range bound@@ -1251,17 +1251,12 @@ ~~~~~~~~~~~~~ Size coercion, written with ``:>``, can be used to perform a-runtime-checked coercion of one size to another. Since size-annotations can refer only to variables and constants, this is-necessary when writing more complicated size functions::+runtime-checked coercion of one size to another. This can be useful+as an escape hatch in the size type system:: def concat_to 'a (m: i32) (a: []a) (b: []a) : [m]a = a ++ b :> [m]a -Only expression-level type annotations give rise to run-time checks.-Despite their similar syntax, parameter and return type annotations-must be valid at compile-time, or type checking will fail.- .. _causality: Causality restriction@@ -1379,39 +1374,7 @@ be passed to ``map``, as explained before. The solution is to bind ``length`` to a name *before* the lambda. -.. _sizes-in-abbreviations: -Sizes in type abbreviations-~~~~~~~~~~~~~~~~~~~~~~~~~~~--When anonymous sizes are passed to type abbreviations, if that-anonymous size is eventually instantiated with an existential size,-the *same* existential size is going to be used for all instances of-the corresponding parameter in the right-hand-side of the type-abbreviation. Note that this breaks with the usual conception of type-abbreviations as purely a shorthand, as this could not be expressed-without the abbreviation. Example::-- type square [n] = [n][n]i32--The following function is be *known* to return a square array::-- val f : () -> square []--But this is not the case for the function that inlines the definition-of ``square``::-- val g : () -> [][]i32--As this above would be elaborated as follows::-- val g : () -> ?[n][m].[n][m]i32--We can of course explicitly write that the function returns a square-array of existential size::-- val g : () -> ?[n].[n]i32- .. _in-place-updates: In-place Updates@@ -1554,7 +1517,7 @@ .. productionlist:: mod_exp: `qualid` : | `mod_exp` ":" `mod_type_exp`- : | "\" "(" `id` ":" `mod_type_exp` ")" [":" `mod_type_exp`] "->" `mod_exp`+ : | "\" "(" `mod_param`* ")" [":" `mod_type_exp`] "->" `mod_exp` : | `mod_exp` `mod_exp` : | "(" `mod_exp` ")" : | "{" `dec`* "}"
docs/man/futhark-test.rst view
@@ -55,8 +55,8 @@ braces must consist of a sequence of Futhark types, including sizes in the case of arrays. When ``futhark test`` is run, a file located in a ``data/`` subdirectory, containing values of the indicated types and-shapes is, automatically constructed with ``futhark-dataset``. Apart-from sizes, integer constants (with or without type suffix), and+shapes is, automatically constructed with :ref:`futhark-dataset(1)`.+Apart from sizes, integer constants (with or without type suffix), and floating-point constants (always with type suffix) are also permitted. If ``input`` is preceded by ``script``, the text between the curly@@ -68,22 +68,21 @@ The only builtin function supported is ``$loaddata``. If ``input`` is followed by an ``@`` and a file name (which must not-contain any whitespace) instead of curly braces, values or-FutharkScript expression will be read from the indicated file. This-is recommended for large data sets. This notation cannot be used with-``random`` input.+contain any whitespace) instead of curly braces, values will be read+from the indicated file. This is recommended for large data sets.+This notation cannot be used with ``random`` input. With ``script+input``, the file contents will be interpreted as a FutharkScript+expression. After the ``input`` block, the expected result of the test case is written as either ``output`` followed by another block of values, or-an expected run-time error, in which a regular expression can be used-to specify the exact error message expected. If no regular expression-is given, any error message is accepted. If neither ``output`` nor-``error`` is given, the program will be expected to execute-succesfully, but its output will not be validated.+``error:`` followed by a regex indicating an expected run-time error.+If neither ``output`` nor ``error`` is given, the program will be+expected to execute succesfully, but its output will not be validated. If ``output`` is preceded by ``auto`` (as in ``auto output``), the expected values are automatically generated by compiling the program-with ``futhark-c`` and recording its result for the given input (which+with ``futhark c`` and recording its result for the given input (which must not fail). This is usually only useful for testing or benchmarking alternative compilers, and not for testing the correctness of Futhark programs. This currently does not work for@@ -157,6 +156,10 @@ -i Test with the interpreter.++-I+ Pass the program through the compiler frontend, but do not run them.+ This is only useful for testing the Futhark compiler itself. -t Type-check the programs, but do not run them.
docs/man/futhark.rst view
@@ -27,9 +27,9 @@ futhark benchcmp FILE_A FILE_B ------------------------------ -Compares two Futhark benchmarks and reports changes in performance.-The files must be formatted in the same manner as a JSON file returned-from :ref:`futhark-bench(1)`.+Compare two JSON files produced by the ``--json`` option of+:ref:`futhark-bench(1)`. The results show speedup of the latter file+compared to the former. futhark check [-w] PROGRAM --------------------------@@ -76,7 +76,7 @@ futhark eval [-f FILE] [-w] <exprs...> -------------------------------------- -Evaluates expressions given as command-line arguments. Optionally +Evaluates expressions given as command-line arguments. Optionally allows a file import using ``-f``. futhark hash PROGRAM
futhark.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: futhark-version: 0.24.3+version: 0.25.1 synopsis: An optimising compiler for a functional, array-oriented language. description: Futhark is a small programming language designed to be compiled to@@ -288,6 +288,7 @@ Futhark.IR.Mem Futhark.IR.Mem.Interval Futhark.IR.Mem.IxFun+ Futhark.IR.Mem.LMAD Futhark.IR.Mem.Simplify Futhark.IR.Parse Futhark.IR.Pretty@@ -321,10 +322,12 @@ Futhark.Internalise.Defunctorise Futhark.Internalise.Entry Futhark.Internalise.Exps+ Futhark.Internalise.FullNormalise Futhark.Internalise.Lambdas Futhark.Internalise.LiftLambdas Futhark.Internalise.Monad Futhark.Internalise.Monomorphise+ Futhark.Internalise.ReplaceRecords Futhark.Internalise.TypesValues Futhark.LSP.Compile Futhark.LSP.Diagnostic@@ -440,6 +443,7 @@ Language.Futhark.Traversals Language.Futhark.Tuple Language.Futhark.TypeChecker+ Language.Futhark.TypeChecker.Consumption Language.Futhark.TypeChecker.Match Language.Futhark.TypeChecker.Modules Language.Futhark.TypeChecker.Monad@@ -481,7 +485,7 @@ , fgl-visualize , file-embed >=0.0.14.0 , filepath >=1.4.1.1- , free >=4.12.4+ , free >=5.1.10 , futhark-data >= 1.1.0.0 , futhark-server >= 1.2.2.1 , futhark-manifest >= 1.2.0.1@@ -490,7 +494,8 @@ , haskeline , language-c-quote >= 0.12 , lens- , lsp >= 1.5.0+ , lsp >= 2.0.0.0+ , lsp-types >= 2.0.0.0 , mainland-pretty >=0.7.1 , cmark-gfm >=0.2.1 , megaparsec >=9.0.0@@ -508,7 +513,7 @@ , time >=1.6.0.1 , transformers >=0.3 , vector >=0.12- , versions >=5.0.0+ , versions >=6.0.0 , zlib >=0.6.1.2 , statistics , mwc-random@@ -531,6 +536,7 @@ Futhark.BenchTests Futhark.Pkg.SolveTests Futhark.Analysis.AlgSimplifyTests+ Futhark.Internalise.TypesValuesTests Futhark.IR.Prop.RearrangeTests Futhark.IR.Prop.ReshapeTests Futhark.IR.PropTests@@ -551,6 +557,7 @@ QuickCheck >=2.8 , base , containers+ , free , futhark , megaparsec , tasty
prelude/array.fut view
@@ -28,12 +28,12 @@ -- | Everything but the first element of the array. -- -- **Complexity:** O(1).-def tail [n] 't (x: [n]t) = x[1:]+def tail [n] 't (x: [n]t): [n-1]t = x[1:] -- | Everything but the last element of the array. -- -- **Complexity:** O(1).-def init [n] 't (x: [n]t) = x[0:n-1]+def init [n] 't (x: [n]t): [n-1]t = x[0:n-1] -- | Take some number of elements from the head of the array. --@@ -43,13 +43,18 @@ -- | Remove some number of elements from the head of the array. -- -- **Complexity:** O(1).-def drop [n] 't (i: i64) (x: [n]t) = x[i:]+def drop [n] 't (i: i64) (x: [n]t): [n-i]t = x[i:] +-- | Statically change the size of an array. Fail at runtime if the+-- imposed size does not match the actual size. Essentially syntactic+-- sugar for a size coercion.+def sized [m] 't (n: i64) (xs: [m]t) : [n]t = xs :> [n]t+ -- | Split an array at a given position. -- -- **Complexity:** O(1).-def split [n] 't (i: i64) (xs: [n]t): ([i]t, []t) =- (xs[0:i], xs[i:])+def split [n][m] 't (xs: [n+m]t): ([n]t, [m]t) =+ (xs[0:n], xs[n:n+m] :> [m]t) -- | Return the elements of the array in reverse order. --@@ -62,28 +67,10 @@ -- **Work:** O(n). -- -- **Span:** O(1).-def (++) [n] [m] 't (xs: [n]t) (ys: [m]t): *[]t = intrinsics.concat xs ys+def (++) [n] [m] 't (xs: [n]t) (ys: [m]t): *[n+m]t = intrinsics.concat xs ys -- | An old-fashioned way of saying `++`.-def concat [n] [m] 't (xs: [n]t) (ys: [m]t): *[]t = xs ++ ys---- | Concatenation where the result has a predetermined size. If the--- provided size is wrong, the function will fail with a run-time--- error.-def concat_to [n] [m] 't (k: i64) (xs: [n]t) (ys: [m]t): *[k]t = xs ++ ys :> [k]t---- | Rotate an array some number of elements to the left. A negative--- rotation amount is also supported.------ For example, if `b==rotate r a`, then `b[x] = a[x+r]`.------ **Work:** O(n).------ **Span:** O(1).------ Note: In most cases, `rotate` will be fused with subsequent--- operations such as `map`, in which case it is free.-def rotate [n] 't (r: i64) (xs: [n]t): [n]t = intrinsics.rotate r xs+def concat [n] [m] 't (xs: [n]t) (ys: [m]t): *[n+m]t = xs ++ ys -- | Construct an array of consecutive integers of the given length, -- starting at 0.@@ -103,6 +90,20 @@ def indices [n] 't (_: [n]t) : *[n]i64 = iota n +-- | Rotate an array some number of elements to the left. A negative+-- rotation amount is also supported.+--+-- For example, if `b==rotate r a`, then `b[x] = a[x+r]`.+--+-- **Work:** O(n).+--+-- **Span:** O(1).+--+-- Note: In most cases, `rotate` will be fused with subsequent+-- operations such as `map`, in which case it is free.+def rotate [n] 't (r: i64) (a: [n]t) =+ map (\i -> #[unsafe] a[(i+r)%n]) (iota n)+ -- | Construct an array of the given length containing the given -- value. --@@ -123,35 +124,30 @@ -- | Combines the outer two dimensions of an array. -- -- **Complexity:** O(1).-def flatten [n][m] 't (xs: [n][m]t): []t =+def flatten [n][m] 't (xs: [n][m]t): [n*m]t = intrinsics.flatten xs --- | Like `flatten`@term, but where the final size is known. Fails at--- runtime if the provided size is wrong.-def flatten_to [n][m] 't (l: i64) (xs: [n][m]t): [l]t =- flatten xs :> [l]t- -- | Like `flatten`, but on the outer three dimensions of an array.-def flatten_3d [n][m][l] 't (xs: [n][m][l]t): []t =+def flatten_3d [n][m][l] 't (xs: [n][m][l]t): [n*m*l]t = flatten (flatten xs) -- | Like `flatten`, but on the outer four dimensions of an array.-def flatten_4d [n][m][l][k] 't (xs: [n][m][l][k]t): []t =+def flatten_4d [n][m][l][k] 't (xs: [n][m][l][k]t): [n*m*l*k]t = flatten (flatten_3d xs) -- | Splits the outer dimension of an array in two. -- -- **Complexity:** O(1).-def unflatten [p] 't (n: i64) (m: i64) (xs: [p]t): [n][m]t =- intrinsics.unflatten n m xs :> [n][m]t+def unflatten 't [n][m] (xs: [n*m]t): [n][m]t =+ intrinsics.unflatten n m xs -- | Like `unflatten`, but produces three dimensions.-def unflatten_3d [p] 't (n: i64) (m: i64) (l: i64) (xs: [p]t): [n][m][l]t =- unflatten n m (unflatten (n*m) l xs)+def unflatten_3d 't [n][m][l] (xs: [n*m*l]t): [n][m][l]t =+ unflatten (unflatten xs) -- | Like `unflatten`, but produces four dimensions.-def unflatten_4d [p] 't (n: i64) (m: i64) (l: i64) (k: i64) (xs: [p]t): [n][m][l][k]t =- unflatten n m (unflatten_3d (n*m) l k xs)+def unflatten_4d 't [n][m][l][k] (xs: [n*m*l*k]t): [n][m][l][k]t =+ unflatten (unflatten_3d xs) -- | Transpose an array. --
prelude/soacs.fut view
@@ -178,7 +178,7 @@ -- **Work:** *O(n ✕ W(p))* -- -- **Span:** *O(log(n) ✕ W(p))*-def partition [n] 'a (p: a -> bool) (as: [n]a): ([]a, []a) =+def partition [n] 'a (p: a -> bool) (as: [n]a): ?[k].([k]a, [n-k]a) = let p' x = if p x then 0 else 1 let (as', is) = intrinsics.partition 2 p' as in (as'[0:is[0]], as'[is[0]:n])@@ -188,10 +188,12 @@ -- **Work:** *O(n ✕ (W(p1) + W(p2)))* -- -- **Span:** *O(log(n) ✕ (W(p1) + W(p2)))*-def partition2 [n] 'a (p1: a -> bool) (p2: a -> bool) (as: [n]a): ([]a, []a, []a) =+def partition2 [n] 'a (p1: a -> bool) (p2: a -> bool) (as: [n]a): ?[k][l].([k]a, [l]a, [n-k-l]a) = let p' x = if p1 x then 0 else if p2 x then 1 else 2 let (as', is) = intrinsics.partition 3 p' as- in (as'[0:is[0]], as'[is[0]:is[0]+is[1]], as'[is[0]+is[1]:n])+ in (as'[0:is[0]],+ as'[is[0]:is[0]+is[1]] :> [is[1]]a,+ as'[is[0]+is[1]:n] :> [n-is[0]-is[1]]a) -- | Return `true` if the given function returns `true` for all -- elements in the array.
src/Futhark/AD/Fwd.hs view
@@ -226,9 +226,6 @@ arr_tan <- tangent arr arrs_tans <- mapM tangent arrs addStm $ Let pat_tan aux $ BasicOp $ Concat d (arr_tan :| arrs_tans) w- Copy arr -> do- arr_tan <- tangent arr- addStm $ Let pat_tan aux $ BasicOp $ Copy arr_tan Manifest ds arr -> do arr_tan <- tangent arr addStm $ Let pat_tan aux $ BasicOp $ Manifest ds arr_tan@@ -245,9 +242,6 @@ Rearrange perm arr -> do arr_tan <- tangent arr addStm $ Let pat_tan aux $ BasicOp $ Rearrange perm arr_tan- Rotate rots arr -> do- arr_tan <- tangent arr- addStm $ Let pat_tan aux $ BasicOp $ Rotate rots arr_tan _ -> error $ "basicFwd: Unsupported op " ++ prettyString op fwdLambda :: Lambda SOACS -> ADM (Lambda SOACS)
src/Futhark/AD/Rev.hs view
@@ -141,14 +141,9 @@ updateAdj arr <=< letExp "adj_rearrange" . BasicOp $ Rearrange (rearrangeInverse perm) pat_adj --- Rotate rots arr -> do+ Replicate (Shape []) (Var se) -> do (_pat_v, pat_adj) <- commonBasicOp pat aux e m- returnSweepCode $ do- let neg = BasicOp . BinOp (Sub Int64 OverflowWrap) (intConst Int64 0)- rots' <- mapM (letSubExp "rot_neg" . neg) rots- void $- updateAdj arr <=< letExp "adj_rotate" . BasicOp $- Rotate rots' pat_adj+ returnSweepCode $ void $ updateAdj se pat_adj -- Replicate (Shape ns) x -> do (_pat_v, pat_adj) <- commonBasicOp pat aux e m@@ -184,10 +179,6 @@ zipWithM_ updateAdj (arr : arrs) slices --- Copy se -> do- (_pat_v, pat_adj) <- commonBasicOp pat aux e m- returnSweepCode $ void $ updateAdj se pat_adj- -- Manifest _ se -> do (_pat_v, pat_adj) <- commonBasicOp pat aux e m returnSweepCode $ void $ updateAdj se pat_adj@@ -211,7 +202,9 @@ t <- lookupType v_adj v_adj_copy <- case t of- Array {} -> letExp "update_val_adj_copy" $ BasicOp $ Copy v_adj+ Array {} ->+ letExp "update_val_adj_copy" . BasicOp $+ Replicate mempty (Var v_adj) _ -> pure v_adj updateSubExpAdj v v_adj_copy zeroes <- letSubExp "update_zero" . zeroExp =<< subExpType v
src/Futhark/AD/Rev/Hist.hs view
@@ -183,7 +183,9 @@ dst_type <- lookupType dst let dst_dims = arrayDims dst_type - dst_cpy <- letExp (baseString dst <> "_copy") $ BasicOp $ Copy dst+ dst_cpy <-+ letExp (baseString dst <> "_copy") . BasicOp $+ Replicate mempty (Var dst) acc_v_p <- newParam "acc_v" $ Prim t acc_i_p <- newParam "acc_i" $ Prim int64@@ -492,7 +494,9 @@ diffAddHist _ops x aux n add ne is vs w rf dst m = do let t = paramDec $ head $ lambdaParams add - dst_cpy <- letExp (baseString dst <> "_copy") $ BasicOp $ Copy dst+ dst_cpy <-+ letExp (baseString dst <> "_copy") . BasicOp $+ Replicate mempty (Var dst) f <- mkIdentityLambda [Prim int64, t] auxing aux . letBindNames [x] . Op $
src/Futhark/AD/Rev/Loop.hs view
@@ -318,7 +318,8 @@ v' <- letExp "restore" $ BasicOp $ Index vs $ fullSlice vs_t [DimFix i_i64'] t <- lookupType v v'' <- case (t, v `elem` consumed) of- (Array {}, True) -> letExp "restore_copy" $ BasicOp $ Copy v'+ (Array {}, True) ->+ letExp "restore_copy" $ BasicOp $ Replicate mempty $ Var v' _ -> pure v' pure $ Just (v, v'') | otherwise = pure Nothing
src/Futhark/AD/Rev/Monad.hs view
@@ -254,7 +254,9 @@ v_t <- lookupType v case v_t of Array {} -> do- v' <- letExp (baseString v <> "_ad_copy") (BasicOp $ Copy v)+ v' <-+ letExp (baseString v <> "_ad_copy") . BasicOp $+ Replicate mempty (Var v) addSubstitution v' v pure [(v, v')] _ -> pure mempty@@ -269,7 +271,11 @@ v_t <- lookupType v case v_t of Acc {} -> error $ "copyConsumedArrsInBody: Acc " <> prettyString v- Array {} -> M.singleton v <$> letExp (baseString v <> "_ad_copy") (BasicOp $ Copy v)+ Array {} ->+ M.singleton v+ <$> letExp+ (baseString v <> "_ad_copy")+ (BasicOp $ Replicate mempty (Var v)) _ -> pure mempty returnSweepCode :: ADM a -> ADM a
src/Futhark/AD/Rev/Reduce.hs view
@@ -27,9 +27,6 @@ slice = fullSlice arr_t [DimSlice start w stride] letExp (baseString arr <> "_rev") $ BasicOp $ Index arr slice -eRotate :: MonadBuilder m => [SubExp] -> VName -> m VName-eRotate rots arr = letExp (baseString arr <> "_rot") $ BasicOp $ Rotate rots arr- scanExc :: (MonadBuilder m, Rep m ~ SOACS) => String ->@@ -40,34 +37,27 @@ w <- arraysSize 0 <$> mapM lookupType arrs form <- scanSOAC [scan] res_incl <- letTupExp (desc <> "_incl") $ Op $ Screma w arrs form- res_incl_rot <- mapM (eRotate [intConst Int64 (-1)]) res_incl iota <- letExp "iota" . BasicOp $ Iota w (intConst Int64 0) (intConst Int64 1) Int64 iparam <- newParam "iota_param" $ Prim int64- vparams <- mapM (newParam "vp") ts- let params = iparam : vparams - body <- runBodyBuilder . localScope (scopeOfLParams params) $ do+ lam <- mkLambda [iparam] $ do let first_elem = eCmpOp (CmpEq int64) (eSubExp (Var (paramName iparam))) (eSubExp (intConst Int64 0))- eBody- [ eIf- first_elem- (resultBodyM nes)- (resultBodyM $ map (Var . paramName) vparams)- ]+ prev = toExp $ le64 (paramName iparam) - 1+ fmap subExpsRes . letTupExp' "scan_ex_res"+ =<< eIf+ first_elem+ (resultBodyM $ scanNeutral scan)+ (eBody $ map (`eIndex` prev) res_incl) - let lam = Lambda params body ts- letTupExp desc $ Op $ Screma w (iota : res_incl_rot) (mapSOAC lam)- where- nes = scanNeutral scan- ts = lambdaReturnType $ scanLambda scan+ letTupExp desc $ Op $ Screma w [iota] (mapSOAC lam) mkF :: Lambda SOACS -> ADM ([VName], Lambda SOACS) mkF lam = do
src/Futhark/AD/Rev/Scatter.hs view
@@ -100,7 +100,9 @@ -- of the program. In that case the asymptotics will not be -- (locally) preserved, but since ys must necessarily have been -- constructed somewhere close, they are probably globally OK.- ys_copy <- letExp (baseString ys <> "_copy") $ BasicOp $ Copy ys+ ys_copy <-+ letExp (baseString ys <> "_copy") . BasicOp $+ Replicate mempty (Var ys) returnSweepCode $ do ys_adj <- lookupAdjVal ys -- computing vs_ctrbs and updating vs_adj
src/Futhark/Analysis/AlgSimplify.hs view
@@ -27,7 +27,7 @@ import Futhark.Analysis.PrimExp import Futhark.Analysis.PrimExp.Convert import Futhark.IR.Prop.Names-import Futhark.IR.Syntax.Core+import Futhark.IR.Syntax.Core (SubExp (..), VName) import Futhark.Util import Futhark.Util.Pretty import Prelude hiding (negate)
src/Futhark/Analysis/Metrics.hs view
@@ -134,14 +134,12 @@ basicOpMetrics FlatIndex {} = seen "FlatIndex" basicOpMetrics FlatUpdate {} = seen "FlatUpdate" basicOpMetrics Concat {} = seen "Concat"-basicOpMetrics Copy {} = seen "Copy" basicOpMetrics Manifest {} = seen "Manifest" basicOpMetrics Iota {} = seen "Iota" basicOpMetrics Replicate {} = seen "Replicate" basicOpMetrics Scratch {} = seen "Scratch" basicOpMetrics Reshape {} = seen "Reshape" basicOpMetrics Rearrange {} = seen "Rearrange"-basicOpMetrics Rotate {} = seen "Rotate" basicOpMetrics UpdateAcc {} = seen "UpdateAcc" -- | Compute metrics for this lambda.
src/Futhark/Analysis/PrimExp/Convert.hs view
@@ -47,7 +47,7 @@ toExp (FunExp h args t) = Apply (nameFromString h) <$> args'- <*> pure [primRetType t]+ <*> pure [(primRetType t, mempty)] <*> pure (Safe, mempty, []) where args' = zip <$> mapM (toSubExp "apply_arg") args <*> pure (repeat Observe)@@ -78,7 +78,7 @@ primExpFromSubExpM f se primExpFromExp f (Apply fname args ts _) | isBuiltInFunction fname,- [Prim t] <- map declExtTypeOf ts =+ [Prim t] <- map (declExtTypeOf . fst) ts = FunExp (nameToString fname) <$> mapM (primExpFromSubExpM f . fst) args <*> pure t primExpFromExp _ _ = fail "Not a PrimExp"
src/Futhark/Analysis/SymbolTable.hs view
@@ -353,9 +353,10 @@ | length ds == length is, Just (Prim t) <- lookupSubExpType v table = Just $ Indexed mempty $ primExpFromSubExp t v-indexExp table (BasicOp (Replicate (Shape [_]) (Var v))) _ (_ : is) = do+indexExp table (BasicOp (Replicate s (Var v))) _ is = do guard $ v `available` table- index' v is table+ guard $ s /= mempty+ index' v (drop (shapeRank s) is) table indexExp table (BasicOp (Reshape _ newshape v)) _ is | Just oldshape <- arrayDims <$> lookupType v table = -- TODO: handle coercions more efficiently.
src/Futhark/CLI/Dev.hs view
@@ -26,8 +26,10 @@ import Futhark.IR.TypeCheck (Checkable, checkProg) import Futhark.Internalise.Defunctionalise as Defunctionalise import Futhark.Internalise.Defunctorise as Defunctorise+import Futhark.Internalise.FullNormalise as FullNormalise import Futhark.Internalise.LiftLambdas as LiftLambdas import Futhark.Internalise.Monomorphise as Monomorphise+import Futhark.Internalise.ReplaceRecords as ReplaceRecords import Futhark.Optimise.ArrayShortCircuiting qualified as ArrayShortCircuiting import Futhark.Optimise.CSE import Futhark.Optimise.DoubleBuffer@@ -74,11 +76,13 @@ Pipeline [UntypedPass] | -- | Partially evaluate away the module language. Defunctorise- | -- | Defunctorise and monomorphise.+ | -- | Defunctorise and normalise.+ FullNormalise+ | -- | Defunctorise, normalise and monomorphise. Monomorphise- | -- | Defunctorise, monomorphise, and lambda-lift.+ | -- | Defunctorise, normalise, monomorphise and lambda-lift. LiftLambdas- | -- | Defunctorise, monomorphise, lambda-lift, and defunctionalise.+ | -- | Defunctorise, normalise, monomorphise, lambda-lift and defunctionalise. Defunctionalise data Config = Config@@ -540,6 +544,11 @@ "Partially evaluate all module constructs and print the residual program.", Option []+ ["normalise"]+ (NoArg $ Right $ \opts -> opts {futharkPipeline = FullNormalise})+ "Fully normalise the program.",+ Option+ [] ["monomorphise"] (NoArg $ Right $ \opts -> opts {futharkPipeline = Monomorphise}) "Monomorphise the program.",@@ -732,12 +741,20 @@ Defunctorise -> do (_, imports, src) <- readProgram' liftIO $ p $ evalState (Defunctorise.transformProg imports) src+ FullNormalise -> do+ (_, imports, src) <- readProgram'+ liftIO $+ p $+ flip evalState src $+ Defunctorise.transformProg imports+ >>= FullNormalise.transformProg Monomorphise -> do (_, imports, src) <- readProgram' liftIO $ p $ flip evalState src $ Defunctorise.transformProg imports+ >>= FullNormalise.transformProg >>= Monomorphise.transformProg LiftLambdas -> do (_, imports, src) <- readProgram'@@ -745,7 +762,9 @@ p $ flip evalState src $ Defunctorise.transformProg imports+ >>= FullNormalise.transformProg >>= Monomorphise.transformProg+ >>= ReplaceRecords.transformProg >>= LiftLambdas.transformProg Defunctionalise -> do (_, imports, src) <- readProgram'@@ -753,7 +772,9 @@ p $ flip evalState src $ Defunctorise.transformProg imports+ >>= FullNormalise.transformProg >>= Monomorphise.transformProg+ >>= ReplaceRecords.transformProg >>= LiftLambdas.transformProg >>= Defunctionalise.transformProg Pipeline {} -> do
src/Futhark/CLI/Doc.hs view
@@ -8,12 +8,13 @@ import Control.Monad.State import Data.FileEmbed import Data.List (nubBy)-import Data.Text.Lazy qualified as T-import Data.Text.Lazy.IO qualified as T+import Data.Text qualified as T+import Data.Text.IO qualified as T+import Data.Text.Lazy qualified as LT import Futhark.Compiler (Imports, dumpError, fileProg, newFutharkConfig, readProgramFiles) import Futhark.Doc.Generator import Futhark.Pipeline (FutharkM, Verbosity (..), runFutharkM)-import Futhark.Util (directoryContents, trim)+import Futhark.Util (directoryContents) import Futhark.Util.Options import Language.Futhark.Semantic (mkInitialImport) import Language.Futhark.Syntax (DocComment (..), progDoc)@@ -65,7 +66,7 @@ (file_htmls, _warnings) = renderFiles direct_imports $ filter (not . ignored) imports- mapM_ (write . fmap renderHtml) file_htmls+ mapM_ (write . fmap (LT.toStrict . renderHtml)) file_htmls write ("style.css", cssFile) where write :: (FilePath, T.Text) -> IO ()@@ -82,7 +83,7 @@ -- recognise them by a file comment containing "ignore". ignored (_, fm) = case progDoc (fileProg fm) of- Just (DocComment s _) -> trim s == "ignore"+ Just (DocComment s _) -> T.strip s == "ignore" _ -> False cssFile :: T.Text
src/Futhark/CLI/Eval.hs view
@@ -14,6 +14,7 @@ import Futhark.Pipeline import Futhark.Util.Options import Futhark.Util.Pretty+import Language.Futhark import Language.Futhark.Interpreter qualified as I import Language.Futhark.Parser import Language.Futhark.Semantic qualified as T@@ -53,7 +54,13 @@ (_, Left terr) -> do hPutDoc stderr $ I.prettyTypeError terr exitWith $ ExitFailure 1- (_, Right (_, e)) -> pure e+ (_, Right ([], e)) -> pure e+ (_, Right (tparams, e)) -> do+ putDocLn $ "Inferred type of expression: " <> align (pretty (typeOf e))+ T.putStrLn $+ "The following types are ambiguous: "+ <> T.intercalate ", " (map (nameToText . toName . typeParamName) tparams)+ exitWith $ ExitFailure 1 pval <- runInterpreterNoBreak $ I.interpretExp ctx fexp case pval of Left err -> do
src/Futhark/CLI/LSP.hs view
@@ -7,13 +7,13 @@ import Data.IORef (newIORef) import Futhark.LSP.Handlers (handlers) import Futhark.LSP.State (emptyState)-import Language.LSP.Server-import Language.LSP.Types+import Language.LSP.Protocol.Types ( SaveOptions (SaveOptions),- TextDocumentSyncKind (TdSyncIncremental),+ TextDocumentSyncKind (TextDocumentSyncKind_Incremental), TextDocumentSyncOptions (..), type (|?) (InR), )+import Language.LSP.Server -- | Run @futhark lsp@ main :: String -> [String] -> IO ()@@ -29,7 +29,7 @@ interpretHandler = \env -> Iso (runLspT env) liftIO, options = defaultOptions- { textDocumentSync = Just syncOptions+ { optTextDocumentSync = Just syncOptions } } pure ()@@ -38,7 +38,7 @@ syncOptions = TextDocumentSyncOptions { _openClose = Just True,- _change = Just TdSyncIncremental,+ _change = Just TextDocumentSyncKind_Incremental, _willSave = Just False, _willSaveWaitUntil = Just False, _save = Just $ InR $ SaveOptions $ Just False
src/Futhark/CLI/Literate.hs view
@@ -690,14 +690,12 @@ exists <- liftIO $ doesFileExist fname liftIO $ createDirectoryIfMissing True $ envImgDir env when (exists && scriptVerbose (envOpts env) > 0) $- liftIO $- T.hPutStrLn stderr $- "Using existing file: " <> T.pack fname+ liftIO . T.hPutStrLn stderr $+ "Using existing file: " <> T.pack fname unless exists $ do when (scriptVerbose (envOpts env) > 0) $- liftIO $- T.hPutStrLn stderr $- "Generating new file: " <> T.pack fname+ liftIO . T.hPutStrLn stderr $+ "Generating new file: " <> T.pack fname m fname modify $ \s -> s {stateFiles = S.insert fname $ stateFiles s} pure (fname, fname_rel)@@ -848,8 +846,8 @@ (progressStep, progressDone) | fancyTerminal, scriptVerbose (envOpts env) > 0 =- ( \j num_frames -> do- liftIO . T.putStr $+ ( \j num_frames -> liftIO $ do+ T.putStr $ "\r" <> progressBar (ProgressBar 40 (fromIntegral num_frames - 1) (fromIntegral j))@@ -858,9 +856,8 @@ <> "/" <> prettyText num_frames <> " "- liftIO $ hFlush stdout,- liftIO $ do- T.putStrLn ""+ hFlush stdout,+ liftIO $ T.putStrLn "" ) | otherwise = (\_ _ -> pure (), pure ())
src/Futhark/CLI/Main.hs view
@@ -79,7 +79,7 @@ ("lsp", (LSP.main, "Run LSP server.")), ("thanks", (Misc.mainThanks, "Express gratitude.")), ("tokens", (Misc.mainTokens, "Print tokens from Futhark file.")),- ("benchcmp", (Benchcmp.main, "Compare two Futhark benchmarks."))+ ("benchcmp", (Benchcmp.main, "Compare two benchmark results.")) ] msg :: String
src/Futhark/CLI/REPL.hs view
@@ -502,7 +502,7 @@ ( "unbreak", ( unbreakCommand, [text|-Skip all future occurences of the current breakpoint.+Skip all future occurrences of the current breakpoint. |] ) ),
src/Futhark/CLI/Test.hs view
@@ -23,7 +23,7 @@ import Futhark.Test import Futhark.Util (atMostChars, fancyTerminal, showText) import Futhark.Util.Options-import Futhark.Util.Pretty (annotate, bold, hardline, pretty, putDoc, vsep)+import Futhark.Util.Pretty (annotate, bgColor, bold, hardline, pretty, putDoc, vsep) import Futhark.Util.Table import System.Console.ANSI (clearFromCursorToScreenEnd, clearLine, cursorUpLine) import System.Console.Terminal.Size qualified as Terminal@@ -107,6 +107,8 @@ TypeCheck | -- | Only compile (do not run). Compile+ | -- | Only internalise (do not run).+ Internalise | -- | Test compiled code. Compiled | -- | Test interpreted code.@@ -247,7 +249,7 @@ ] case testAction testcase of CompileTimeFailure expected_error ->- context checkctx $ do+ unless (mode == Internalise) . context checkctx $ do (code, _, err) <- liftIO $ readProcessWithExitCode futhark ["check", program] "" case code of@@ -255,15 +257,23 @@ ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark ExitFailure 1 -> throwError $ T.decodeUtf8 err ExitFailure _ -> liftExcept $ checkError expected_error $ T.decodeUtf8 err- RunCases {} | mode == TypeCheck -> do- let options = ["check", program] ++ configExtraCompilerOptions progs- context checkctx $ do- (code, _, err) <- liftIO $ readProcessWithExitCode futhark options ""-- case code of- ExitSuccess -> pure ()- ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark- ExitFailure _ -> throwError $ T.decodeUtf8 err+ RunCases {}+ | mode == TypeCheck -> do+ let options = ["check", program] ++ configExtraCompilerOptions progs+ context checkctx $ do+ (code, _, err) <- liftIO $ readProcessWithExitCode futhark options ""+ case code of+ ExitSuccess -> pure ()+ ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+ ExitFailure _ -> throwError $ T.decodeUtf8 err+ | mode == Internalise -> do+ let options = ["dev", program] ++ configExtraCompilerOptions progs+ context checkctx $ do+ (code, _, err) <- liftIO $ readProcessWithExitCode futhark options ""+ case code of+ ExitSuccess -> pure ()+ ExitFailure 127 -> throwError $ progNotFound $ T.pack futhark+ ExitFailure _ -> throwError $ T.decodeUtf8 err RunCases ios structures warnings -> do -- Compile up-front and reuse same executable for several entry points. let backend = configBackend progs@@ -567,7 +577,7 @@ when fancy moveCursorToTableTop clear putDoc $- annotate bold (pretty (testCaseProgram test) <> ":")+ annotate (bold <> bgColor Red) (pretty (testCaseProgram test) <> ":") <> hardline <> vsep (map pretty s) <> hardline@@ -694,12 +704,17 @@ "c" ["compiled"] (NoArg $ Right $ \config -> config {configTestMode = Compiled})- "Only run compiled code",+ "Only run compiled code (the default)", Option "C" ["compile"] (NoArg $ Right $ \config -> config {configTestMode = Compile}) "Only compile, do not run.",+ Option+ "I"+ ["internalise"]+ (NoArg $ Right $ \config -> config {configTestMode = Internalise})+ "Only run the compiler frontend.", Option [] ["no-terminal", "notty"]
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -174,14 +174,15 @@ return ret; } + long long new_usage = ctx->$id:usagename + size; if (ctx->detail_memory) { fprintf(ctx->log, "Allocating %lld bytes for %s in %s (then allocated: %lld bytes)", (long long) size, desc, $string:spacedesc,- (long long) ctx->$id:usagename + size);+ new_usage); }- if (ctx->$id:usagename > ctx->$id:peakname) {- ctx->$id:peakname = ctx->$id:usagename;+ if (new_usage > ctx->$id:peakname) {+ ctx->$id:peakname = new_usage; if (ctx->detail_memory) { fprintf(ctx->log, " (new peak).\n"); }@@ -196,7 +197,7 @@ *(block->references) = 1; block->size = size; block->desc = desc;- ctx->$id:usagename += size;+ ctx->$id:usagename = new_usage; return FUTHARK_SUCCESS; } else { // We are naively assuming that any memory allocation error is due to OOM.
src/Futhark/CodeGen/Backends/GenericC/Types.hs view
@@ -415,7 +415,7 @@ in ( storageSize pt rank shape', storeValueHeader sign pt rank shape' [C.cexp|out|] ++ [C.cstms|ret |= $id:values_array(ctx, obj->$id:field, (void*)out);- out += $exp:num_elems * $int:(primByteSize pt::Int);|]+ out += $exp:num_elems * sizeof($ty:(primStorageType pt));|] ) ctx_ty <- contextType@@ -452,7 +452,7 @@ stms $ loadValueHeader sign pt rank [C.cexp|$id:shapearr|] [C.cexp|src|] item [C.citem|const void* $id:dataptr = src;|] stm [C.cstm|obj->$id:field = NULL;|]- stm [C.cstm|src += $exp:num_elems * $int:(primByteSize pt::Int);|]+ stm [C.cstm|src += $exp:num_elems * sizeof($ty:(primStorageType pt));|] pure [C.cstms| obj->$id:field = $id:new_array(ctx, $id:dataptr, $args:dims);
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -1115,7 +1115,7 @@ Var "np.nan" | otherwise = simpleCall "np.float64" [Float $ fromRational $ toRational v] compilePrimValue (BoolValue v) = Bool v-compilePrimValue UnitValue = Var "None"+compilePrimValue UnitValue = Var "np.byte(0)" compileVar :: VName -> CompilerM op s PyExp compileVar v = asks $ fromMaybe (Var v') . M.lookup v' . envVarExp
src/Futhark/CodeGen/Backends/PyOpenCL.hs view
@@ -386,7 +386,7 @@ Call (Var "cl.array.Array") [ Arg $ Var "self.queue",- Arg $ Tuple dims',+ Arg $ Tuple $ dims' <> [Integer 0 | bt == Imp.Unit], Arg $ Var $ Py.compilePrimToExtNp bt ept, ArgKeyword "data" mem' ]
src/Futhark/CodeGen/Backends/SimpleRep.hs view
@@ -251,11 +251,10 @@ storageSize pt rank shape = [C.cexp|$int:header_size + $int:rank * sizeof(typename int64_t) +- $exp:(cproduct dims) * $int:pt_size|]+ $exp:(cproduct dims) * sizeof($ty:(primStorageType pt))|] where- header_size, pt_size :: Int+ header_size :: Int header_size = 1 + 1 + 1 + 4 -- 'b' <version> <num_dims> <type>- pt_size = primByteSize pt dims = [[C.cexp|$exp:shape[$int:i]|] | i <- [0 .. rank - 1]] typeStr :: Signedness -> PrimType -> String
src/Futhark/CodeGen/ImpGen.hs view
@@ -90,7 +90,6 @@ dPrimVE, dIndexSpace, dIndexSpace',- rotateIndex, sFor, sWhile, sComment,@@ -684,7 +683,7 @@ Just (x, y, z) -> (Just x, Just y, Just z) compile = do (inparams, arrayds, args) <- compileInParams types params params_entry- (results, outparams, dests) <- compileOutParams types rettype ret_entry+ (results, outparams, dests) <- compileOutParams types (map fst rettype) ret_entry addFParams params addArrays arrayds @@ -937,6 +936,8 @@ slice' = fmap pe64 slice defCompileBasicOp (Pat [pe]) (Replicate shape se) | Acc {} <- patElemType pe = pure ()+ | shape == mempty =+ copyDWIM (patElemName pe) [] se [] | otherwise = sLoopNest shape $ \is -> copyDWIMFix (patElemName pe) is se [] defCompileBasicOp _ Scratch {} =@@ -951,8 +952,6 @@ BinOpExp (Add it OverflowUndef) e' $ BinOpExp (Mul it OverflowUndef) i' s' copyDWIM (patElemName pe) [DimFix i] (Var (tvVar x)) []-defCompileBasicOp (Pat [pe]) (Copy src) =- copyDWIM (patElemName pe) [] (Var src) [] defCompileBasicOp (Pat [pe]) (Manifest _ src) = copyDWIM (patElemName pe) [] (Var src) [] defCompileBasicOp (Pat [pe]) (Concat i (x :| ys) _) = do@@ -988,13 +987,6 @@ isLiteral _ = Nothing defCompileBasicOp _ Rearrange {} = pure ()-defCompileBasicOp (Pat [pe]) (Rotate rs arr) = do- shape <- arrayShape <$> lookupType arr- sLoopNest shape $ \is -> do- is' <- sequence $ zipWith3 rotate (shapeDims shape) rs is- copyDWIMFix (patElemName pe) is (Var arr) is'- where- rotate d r i = dPrimVE "rot_i" $ rotateIndex (pe64 d) (pe64 r) i defCompileBasicOp _ Reshape {} = pure () defCompileBasicOp _ (UpdateAcc acc is vs) = sComment "UpdateAcc" $ do@@ -1755,13 +1747,6 @@ in foldl1 (.&&.) $ zipWith condInBounds slice dims --- Building blocks for constructing code.--rotateIndex ::- Imp.TExp Int64 ->- Imp.TExp Int64 ->- Imp.TExp Int64 ->- Imp.TExp Int64-rotateIndex d r i = (i + r) `mod` d sFor' :: VName -> Imp.Exp -> ImpM rep r op () -> ImpM rep r op () sFor' i bound body = do
src/Futhark/CodeGen/ImpGen/GPU.hs view
@@ -235,10 +235,6 @@ | Acc {} <- patElemType pe = pure () | otherwise = sReplicate (patElemName pe) se-expCompiler (Pat [pe]) (BasicOp (Rotate rs arr))- | Acc {} <- patElemType pe = pure ()- | otherwise =- sRotateKernel (patElemName pe) (map pe64 rs) arr -- Allocation in the "local" space is just a placeholder. expCompiler _ (Op (Alloc _ (Space "local"))) = pure ()@@ -309,7 +305,15 @@ mapTransposeFunction :: PrimType -> Imp.Function Imp.HostOp mapTransposeFunction bt =- Imp.Function Nothing [] params transpose_code+ Imp.Function Nothing [] params $+ Imp.DebugPrint ("\n# Transpose " <> prettyString bt) Nothing+ <> Imp.DebugPrint "Number of arrays " (Just $ untyped $ Imp.le64 num_arrays)+ <> Imp.DebugPrint "X elements " (Just $ untyped $ Imp.le64 x)+ <> Imp.DebugPrint "Y elements " (Just $ untyped $ Imp.le64 y)+ <> Imp.DebugPrint "Source offset" (Just $ untyped $ Imp.le64 srcoffset)+ <> Imp.DebugPrint "Destination offset" (Just $ untyped $ Imp.le64 destoffset)+ <> transpose_code+ <> Imp.DebugPrint "" Nothing where params = [ memparam destmem,@@ -323,7 +327,7 @@ space = Space "device" memparam v = Imp.MemParam v space- intparam v = Imp.ScalarParam v $ IntType Int32+ intparam v = Imp.ScalarParam v $ IntType Int64 [ destmem, destoffset,@@ -334,7 +338,8 @@ y, mulx, muly,- block+ block,+ use_32b ] = zipWith (VName . nameFromString)@@ -349,7 +354,8 @@ -- transpose kernels "mulx", "muly",- "block"+ "block",+ "use_32b" ] [0 ..] @@ -361,18 +367,23 @@ -- When an input array has either width==1 or height==1, performing a -- transpose will be the same as performing a copy. can_use_copy =- let onearr = Imp.le32 num_arrays .==. 1- height_is_one = Imp.le32 y .==. 1- width_is_one = Imp.le32 x .==. 1+ let onearr = Imp.le64 num_arrays .==. 1+ height_is_one = Imp.le64 y .==. 1+ width_is_one = Imp.le64 x .==. 1 in onearr .&&. (width_is_one .||. height_is_one) transpose_code = Imp.If input_is_empty mempty $ mconcat- [ Imp.DeclareScalar muly Imp.Nonvolatile (IntType Int32),- Imp.SetScalar muly $ untyped $ block_dim `quot` Imp.le32 x,- Imp.DeclareScalar mulx Imp.Nonvolatile (IntType Int32),- Imp.SetScalar mulx $ untyped $ block_dim `quot` Imp.le32 y,+ [ Imp.DeclareScalar muly Imp.Nonvolatile (IntType Int64),+ Imp.SetScalar muly $ untyped $ block_dim `quot` Imp.le64 x,+ Imp.DeclareScalar mulx Imp.Nonvolatile (IntType Int64),+ Imp.SetScalar mulx $ untyped $ block_dim `quot` Imp.le64 y,+ Imp.DeclareScalar use_32b Imp.Nonvolatile Bool,+ Imp.SetScalar use_32b $+ untyped $+ (le64 destoffset + le64 num_arrays * le64 x * le64 y) .<=. 2 ^ (31 :: Int) - 1+ .&&. (le64 srcoffset + le64 num_arrays * le64 x * le64 y) .<=. 2 ^ (31 :: Int) - 1, Imp.If can_use_copy copy_code $ Imp.If should_use_lowwidth (callTransposeKernel TransposeLowWidth) $ Imp.If should_use_lowheight (callTransposeKernel TransposeLowHeight) $@@ -381,47 +392,91 @@ ] input_is_empty =- Imp.le32 num_arrays .==. 0 .||. Imp.le32 x .==. 0 .||. Imp.le32 y .==. 0+ Imp.le64 num_arrays .==. 0 .||. Imp.le64 x .==. 0 .||. Imp.le64 y .==. 0 should_use_small =- Imp.le32 x .<=. (block_dim `quot` 2)- .&&. Imp.le32 y .<=. (block_dim `quot` 2)+ Imp.le64 x .<=. (block_dim `quot` 2)+ .&&. Imp.le64 y .<=. (block_dim `quot` 2) should_use_lowwidth =- Imp.le32 x .<=. (block_dim `quot` 2)- .&&. block_dim .<. Imp.le32 y+ Imp.le64 x .<=. (block_dim `quot` 2)+ .&&. block_dim .<. Imp.le64 y should_use_lowheight =- Imp.le32 y .<=. (block_dim `quot` 2)- .&&. block_dim .<. Imp.le32 x+ Imp.le64 y .<=. (block_dim `quot` 2)+ .&&. block_dim .<. Imp.le64 x copy_code =- let num_bytes = sExt64 $ Imp.le32 x * Imp.le32 y * primByteSize bt+ let num_bytes = sExt64 $ Imp.le64 x * Imp.le64 y * primByteSize bt in Imp.Copy bt destmem- (Imp.Count $ sExt64 $ Imp.le32 destoffset)+ (Imp.Count $ Imp.le64 destoffset) space srcmem- (Imp.Count $ sExt64 $ Imp.le32 srcoffset)+ (Imp.Count $ Imp.le64 srcoffset) space (Imp.Count num_bytes) - callTransposeKernel =+ callTransposeKernel which =+ Imp.If+ (isBool (LeafExp use_32b Bool))+ ( Imp.DebugPrint "Using 32-bit indexing" Nothing+ <> callTransposeKernel32 which+ )+ ( Imp.DebugPrint "Using 64-bit indexing" Nothing+ <> callTransposeKernel64 which+ )++ callTransposeKernel64 = Imp.Op . Imp.CallKernel . mapTransposeKernel+ (int64, le64) (mapTransposeName bt) block_dim_int ( destmem,- Imp.le32 destoffset,+ le64 destoffset, srcmem,- Imp.le32 srcoffset,- Imp.le32 x,- Imp.le32 y,- Imp.le32 mulx,- Imp.le32 muly,- Imp.le32 num_arrays,+ le64 srcoffset,+ le64 x,+ le64 y,+ le64 mulx,+ le64 muly,+ le64 num_arrays, block ) bt++ callTransposeKernel32 =+ Imp.Op+ . Imp.CallKernel+ . mapTransposeKernel+ (int32, le32)+ (mapTransposeName bt)+ block_dim_int+ ( destmem,+ sExt32 (le64 destoffset),+ srcmem,+ sExt32 (le64 srcoffset),+ sExt32 (le64 x),+ sExt32 (le64 y),+ sExt32 (le64 mulx),+ sExt32 (le64 muly),+ sExt32 (le64 num_arrays),+ block+ )+ bt++-- Note [32-bit transpositions]+--+-- Transposition kernels are much slower when they have to use 64-bit+-- arithmetic. I observed about 0.67x slowdown on an A100 GPU when+-- transposing four-byte elements (much less when transposing 8-byte+-- elements). Unfortunately, 64-bit arithmetic is a requirement for+-- large arrays (see #1953 for what happens otherwise). We generate+-- both 32- and 64-bit index arithmetic versions of transpositions,+-- and dynamically pick between them at runtime. This is an+-- unfortunate code bloat, and it would be preferable if we could+-- simply optimise the 64-bit version to make this distinction+-- unnecessary. Fortunately these kernels are quite small.
src/Futhark/CodeGen/ImpGen/GPU/Base.hs view
@@ -34,7 +34,6 @@ -- * Host-level bulk operations sReplicate, sIota,- sRotateKernel, sCopy, -- * Atomics@@ -1397,28 +1396,6 @@ tmp <- tvVar <$> dPrim "tmp" pt emit $ Imp.Read tmp srcmem srcidx pt srcspace Imp.Nonvolatile emit $ Imp.Write destmem destidx pt destspace Imp.Nonvolatile $ Imp.var tmp pt---- | Perform a Rotate with a kernel.-sRotateKernel :: VName -> [Imp.TExp Int64] -> VName -> CallKernelGen ()-sRotateKernel dest rs src = do- t <- lookupType src- let ds = map pe64 $ arrayDims t- n <- dPrimVE "rotate_n" $ product ds- (virtualise, constants) <- simpleKernelConstants n "rotate"-- fname <- askFunction- let name =- keyWithEntryPoint fname $- nameFromString $- "rotate_" ++ show (baseTag $ kernelGlobalThreadIdVar constants)-- sKernelFailureTolerant True threadOperations constants name $- virtualise $ \gtid -> sWhen (gtid .<. n) $ do- is' <- dIndexSpace' "rep_i" ds gtid- is'' <- sequence $ zipWith3 rotate ds rs is'- copyDWIMFix dest is' (Var src) is''- where- rotate d r i = dPrimVE "rot_i" $ rotateIndex d r i compileThreadResult :: SegSpace ->
src/Futhark/CodeGen/ImpGen/GPU/Group.hs view
@@ -286,7 +286,7 @@ ltid <- kernelLocalThreadId . kernelConstants <$> askEnv sWhen (ltid .==. 0) $ updateAcc acc is vs sOp $ Imp.Barrier Imp.FenceLocal-compileGroupExp (Pat [dest]) (BasicOp (Replicate ds se)) = do+compileGroupExp (Pat [dest]) (BasicOp (Replicate ds se)) | ds /= mempty = do flat <- newVName "rep_flat" is <- replicateM (arrayRank dest_t) (newVName "rep_i") let is' = map le64 is@@ -295,14 +295,6 @@ sOp $ Imp.Barrier Imp.FenceLocal where dest_t = patElemType dest-compileGroupExp (Pat [dest]) (BasicOp (Rotate rs arr)) = do- ds <- map pe64 . arrayDims <$> lookupType arr- groupCoverSpace ds $ \is -> do- is' <- sequence $ zipWith3 rotate ds rs is- copyDWIMFix (patElemName dest) is (Var arr) is'- sOp $ Imp.Barrier Imp.FenceLocal- where- rotate d r i = dPrimVE "rot_i" $ rotateIndex d (pe64 r) i compileGroupExp (Pat [dest]) (BasicOp (Iota n e s it)) = do n' <- toExp n e' <- toExp e@@ -695,8 +687,6 @@ onStm (Let (Pat [pe]) _ (BasicOp (Replicate {}))) = S.singleton $ arrayDims $ patElemType pe onStm (Let (Pat [pe]) _ (BasicOp (Iota {}))) =- S.singleton $ arrayDims $ patElemType pe- onStm (Let (Pat [pe]) _ (BasicOp (Copy {}))) = S.singleton $ arrayDims $ patElemType pe onStm (Let (Pat [pe]) _ (BasicOp (Manifest {}))) = S.singleton $ arrayDims $ patElemType pe
src/Futhark/CodeGen/ImpGen/GPU/Transpose.hs view
@@ -7,8 +7,9 @@ ) where +-- See also Note [32-bit transpositions].+ import Futhark.CodeGen.ImpCode.GPU-import Futhark.IR.Prop.Types import Futhark.Util.IntegralExp (divUp, quot, rem) import Prelude hiding (quot, rem) @@ -23,98 +24,98 @@ deriving (Eq, Ord, Show) -- | The types of the arguments accepted by a transposition function.-type TransposeArgs =+type TransposeArgs int = ( VName,- TExp Int32,+ TExp int, VName,- TExp Int32,- TExp Int32,- TExp Int32,- TExp Int32,- TExp Int32,- TExp Int32,+ TExp int,+ TExp int,+ TExp int,+ TExp int,+ TExp int,+ TExp int, VName ) -elemsPerThread :: TExp Int32-elemsPerThread = 4+elemsPerThread :: Num a => a+elemsPerThread = 8 -mapTranspose :: TExp Int32 -> TransposeArgs -> PrimType -> TransposeType -> KernelCode-mapTranspose block_dim args t kind =+mapTranspose :: forall int. IntExp int => (PrimType, VName -> TExp int) -> TExp int -> TransposeArgs int -> PrimType -> TransposeType -> KernelCode+mapTranspose (int, le) block_dim args t kind = case kind of TransposeSmall -> mconcat [ get_ids,- dec our_array_offset $ le32 get_global_id_0 `quot` (height * width) * (height * width),- dec x_index $ (le32 get_global_id_0 `rem` (height * width)) `quot` height,- dec y_index $ le32 get_global_id_0 `rem` height,+ dec our_array_offset $ le get_global_id_0 `quot` (height * width) * (height * width),+ dec x_index $ (le get_global_id_0 `rem` (height * width)) `quot` height,+ dec y_index $ le get_global_id_0 `rem` height, DeclareScalar val Nonvolatile t, dec odata_offset $- (basic_odata_offset `quot` primByteSize t) + le32 our_array_offset,+ (basic_odata_offset `quot` primByteSize t) + le our_array_offset, dec idata_offset $- (basic_idata_offset `quot` primByteSize t) + le32 our_array_offset,- dec index_in $ le32 y_index * width + le32 x_index,- dec index_out $ le32 x_index * height + le32 y_index,+ (basic_idata_offset `quot` primByteSize t) + le our_array_offset,+ dec index_in $ le y_index * width + le x_index,+ dec index_out $ le x_index * height + le y_index, when- (le32 get_global_id_0 .<. width * height * num_arrays)+ (le get_global_id_0 .<. width * height * num_arrays) ( mconcat- [ Read val idata (elements $ sExt64 $ le32 idata_offset + le32 index_in) t (Space "global") Nonvolatile,- Write odata (elements $ sExt64 $ le32 odata_offset + le32 index_out) t (Space "global") Nonvolatile (var val t)+ [ Read val idata (elements $ toOffset $ le idata_offset + le index_in) t (Space "global") Nonvolatile,+ Write odata (elements $ toOffset $ le odata_offset + le index_out) t (Space "global") Nonvolatile (var val t) ] ) ] TransposeLowWidth -> mkTranspose $ lowDimBody- (le32 get_group_id_0 * block_dim + (le32 get_local_id_0 `quot` muly))- ( le32 get_group_id_1 * block_dim * muly- + le32 get_local_id_1- + (le32 get_local_id_0 `rem` muly) * block_dim+ (le get_group_id_0 * block_dim + (le get_local_id_0 `quot` muly))+ ( le get_group_id_1 * block_dim * muly+ + le get_local_id_1+ + (le get_local_id_0 `rem` muly) * block_dim )- ( le32 get_group_id_1 * block_dim * muly- + le32 get_local_id_0- + (le32 get_local_id_1 `rem` muly) * block_dim+ ( le get_group_id_1 * block_dim * muly+ + le get_local_id_0+ + (le get_local_id_1 `rem` muly) * block_dim )- (le32 get_group_id_0 * block_dim + (le32 get_local_id_1 `quot` muly))+ (le get_group_id_0 * block_dim + (le get_local_id_1 `quot` muly)) TransposeLowHeight -> mkTranspose $ lowDimBody- ( le32 get_group_id_0 * block_dim * mulx- + le32 get_local_id_0- + (le32 get_local_id_1 `rem` mulx) * block_dim+ ( le get_group_id_0 * block_dim * mulx+ + le get_local_id_0+ + (le get_local_id_1 `rem` mulx) * block_dim )- (le32 get_group_id_1 * block_dim + (le32 get_local_id_1 `quot` mulx))- (le32 get_group_id_1 * block_dim + (le32 get_local_id_0 `quot` mulx))- ( le32 get_group_id_0 * block_dim * mulx- + le32 get_local_id_1- + (le32 get_local_id_0 `rem` mulx) * block_dim+ (le get_group_id_1 * block_dim + (le get_local_id_1 `quot` mulx))+ (le get_group_id_1 * block_dim + (le get_local_id_0 `quot` mulx))+ ( le get_group_id_0 * block_dim * mulx+ + le get_local_id_1+ + (le get_local_id_0 `rem` mulx) * block_dim ) TransposeNormal -> mkTranspose $ mconcat- [ dec x_index $ le32 get_global_id_0,- dec y_index $ le32 get_group_id_1 * tile_dim + le32 get_local_id_1,+ [ dec x_index $ le get_global_id_0,+ dec y_index $ le get_group_id_1 * tile_dim + le get_local_id_1, DeclareScalar val Nonvolatile t,- when (le32 x_index .<. width) $- For j (untyped elemsPerThread) $- let i = le32 j * (tile_dim `quot` elemsPerThread)+ when (le x_index .<. width) $+ For j (untyped (elemsPerThread :: TExp int)) $+ let i = le j * (tile_dim `quot` elemsPerThread) in mconcat- [ dec index_in $ (le32 y_index + i) * width + le32 x_index,- when (le32 y_index + i .<. height) $+ [ dec index_in $ (le y_index + i) * width + le x_index,+ when (le y_index + i .<. height) $ mconcat [ Read val idata- (elements $ sExt64 $ le32 idata_offset + le32 index_in)+ (elements $ toOffset $ le idata_offset + le index_in) t (Space "global") Nonvolatile, Write block ( elements $- sExt64 $- (le32 get_local_id_1 + i) * (tile_dim + 1)- + le32 get_local_id_0+ toOffset $+ (le get_local_id_1 + i) * (tile_dim + 1)+ + le get_local_id_0 ) t (Space "local")@@ -123,27 +124,27 @@ ] ], Op $ Barrier FenceLocal,- SetScalar x_index $ untyped $ le32 get_group_id_1 * tile_dim + le32 get_local_id_0,- SetScalar y_index $ untyped $ le32 get_group_id_0 * tile_dim + le32 get_local_id_1,- when (le32 x_index .<. height) $- For j (untyped elemsPerThread) $- let i = le32 j * (tile_dim `quot` elemsPerThread)+ SetScalar x_index $ untyped $ le get_group_id_1 * tile_dim + le get_local_id_0,+ SetScalar y_index $ untyped $ le get_group_id_0 * tile_dim + le get_local_id_1,+ when (le x_index .<. height) $+ For j (untyped (elemsPerThread :: TExp int)) $+ let i = le j * (tile_dim `quot` elemsPerThread) in mconcat- [ dec index_out $ (le32 y_index + i) * height + le32 x_index,- when (le32 y_index + i .<. width) $+ [ dec index_out $ (le y_index + i) * height + le x_index,+ when (le y_index + i .<. width) $ mconcat [ Read val block- ( elements . sExt64 $- le32 get_local_id_0 * (tile_dim + 1) + le32 get_local_id_1 + i+ ( elements . toOffset $+ le get_local_id_0 * (tile_dim + 1) + le get_local_id_1 + i ) t (Space "local") Nonvolatile, Write odata- (elements $ sExt64 $ le32 odata_offset + le32 index_out)+ (elements $ toOffset $ le odata_offset + le index_out) t (Space "global") Nonvolatile@@ -152,6 +153,9 @@ ] ] where+ toOffset :: TExp int -> TExp Int64+ toOffset = sExt64+ dec v (TPrimExp e) = DeclareScalar v Nonvolatile (primExpType e) <> SetScalar v e tile_dim = 2 * block_dim@@ -216,30 +220,30 @@ get_ids = mconcat- [ DeclareScalar get_local_id_0 Nonvolatile int32,+ [ DeclareScalar get_local_id_0 Nonvolatile int, Op $ GetLocalId get_local_id_0 0,- DeclareScalar get_local_id_1 Nonvolatile int32,+ DeclareScalar get_local_id_1 Nonvolatile int, Op $ GetLocalId get_local_id_1 1,- DeclareScalar get_group_id_0 Nonvolatile int32,+ DeclareScalar get_group_id_0 Nonvolatile int, Op $ GetGroupId get_group_id_0 0,- DeclareScalar get_group_id_1 Nonvolatile int32,+ DeclareScalar get_group_id_1 Nonvolatile int, Op $ GetGroupId get_group_id_1 1,- DeclareScalar get_group_id_2 Nonvolatile int32,+ DeclareScalar get_group_id_2 Nonvolatile int, Op $ GetGroupId get_group_id_2 2,- DeclareScalar get_local_size_0 Nonvolatile int32,+ DeclareScalar get_local_size_0 Nonvolatile int, Op $ GetLocalSize get_local_size_0 0,- DeclareScalar get_global_id_0 Nonvolatile int32,- SetScalar get_global_id_0 $ untyped $ le32 get_group_id_0 * le32 get_local_size_0 + le32 get_local_id_0+ DeclareScalar get_global_id_0 Nonvolatile int,+ SetScalar get_global_id_0 $ untyped $ le get_group_id_0 * le get_local_size_0 + le get_local_id_0 ] mkTranspose body = mconcat [ get_ids,- dec our_array_offset $ le32 get_group_id_2 * width * height,+ dec our_array_offset $ le get_group_id_2 * width * height, dec odata_offset $- (basic_odata_offset `quot` primByteSize t) + le32 our_array_offset,+ (basic_odata_offset `quot` primByteSize t) + le our_array_offset, dec idata_offset $- (basic_idata_offset `quot` primByteSize t) + le32 our_array_offset,+ (basic_idata_offset `quot` primByteSize t) + le our_array_offset, body ] @@ -248,19 +252,19 @@ [ dec x_index x_in_index, dec y_index y_in_index, DeclareScalar val Nonvolatile t,- dec index_in $ le32 y_index * width + le32 x_index,- when (le32 x_index .<. width .&&. le32 y_index .<. height) $+ dec index_in $ le y_index * width + le x_index,+ when (le x_index .<. width .&&. le y_index .<. height) $ mconcat [ Read val idata- (elements $ sExt64 $ le32 idata_offset + le32 index_in)+ (elements $ sExt64 $ le idata_offset + le index_in) t (Space "global") Nonvolatile, Write block- (elements $ sExt64 $ le32 get_local_id_1 * (block_dim + 1) + le32 get_local_id_0)+ (elements $ sExt64 $ le get_local_id_1 * (block_dim + 1) + le get_local_id_0) t (Space "local") Nonvolatile@@ -269,19 +273,19 @@ Op $ Barrier FenceLocal, SetScalar x_index $ untyped x_out_index, SetScalar y_index $ untyped y_out_index,- dec index_out $ le32 y_index * height + le32 x_index,- when (le32 x_index .<. height .&&. le32 y_index .<. width) $+ dec index_out $ le y_index * height + le x_index,+ when (le x_index .<. height .&&. le y_index .<. width) $ mconcat [ Read val block- (elements $ sExt64 $ le32 get_local_id_0 * (block_dim + 1) + le32 get_local_id_1)+ (elements $ toOffset $ le get_local_id_0 * (block_dim + 1) + le get_local_id_1) t (Space "local") Nonvolatile, Write odata- (elements $ sExt64 (le32 odata_offset + le32 index_out))+ (elements $ toOffset (le odata_offset + le index_out)) t (Space "global") Nonvolatile@@ -289,6 +293,21 @@ ] ] +lowDimKernelAndGroupSize ::+ IntExp int =>+ TExp int ->+ TExp int ->+ TExp int ->+ TExp int ->+ ([TExp int], [TExp int])+lowDimKernelAndGroupSize block_dim num_arrays x_elems y_elems =+ ( [ x_elems `divUp` block_dim,+ y_elems `divUp` block_dim,+ num_arrays+ ],+ [block_dim, block_dim, 1]+ )+ -- | Generate a transpose kernel. There is special support to handle -- input arrays with low width, low height, or both. --@@ -313,38 +332,41 @@ -- an additional element prevents bank conflicts from occuring when -- the tile is accessed column-wise. mapTransposeKernel ::+ forall int.+ IntExp int =>+ (PrimType, VName -> TExp int) -> String -> Integer ->- TransposeArgs ->+ TransposeArgs int -> PrimType -> TransposeType -> Kernel-mapTransposeKernel desc block_dim_int args t kind =+mapTransposeKernel (int, le) desc block_dim_int args t kind = Kernel { kernelBody = DeclareMem block (Space "local") <> Op (LocalAlloc block block_size)- <> mapTranspose block_dim args t kind,+ <> mapTranspose (int, le) block_dim args t kind, kernelUses = uses, kernelNumGroups = map untyped num_groups, kernelGroupSize = map (Left . untyped) group_size,- kernelName = nameFromString name,+ kernelName = nameFromString (name <> "_" <> prettyString int), kernelFailureTolerant = True, kernelCheckLocalMemory = False } where pad2DBytes k = k * (k + 1) * primByteSize t+ block_size :: Count Bytes (TExp Int64) block_size = bytes $ case kind of- TransposeSmall -> 1 :: TExp Int64- -- Not used, but AMD's OpenCL- -- does not like zero-size- -- local memory.+ TransposeSmall -> 1+ -- Not used, but AMD's OpenCL does not like zero-size local+ -- memory. TransposeNormal -> fromInteger $ pad2DBytes $ 2 * block_dim_int TransposeLowWidth -> fromInteger $ pad2DBytes block_dim_int TransposeLowHeight -> fromInteger $ pad2DBytes block_dim_int- block_dim = fromInteger block_dim_int :: TExp Int32+ block_dim = fromInteger block_dim_int :: TExp int ( odata, basic_odata_offset,@@ -379,7 +401,7 @@ uses = map- (`ScalarUse` int32)+ (`ScalarUse` IntType Int64) ( namesToList $ mconcat $ map@@ -401,17 +423,3 @@ TransposeLowHeight -> desc ++ "_low_height" TransposeLowWidth -> desc ++ "_low_width" TransposeNormal -> desc--lowDimKernelAndGroupSize ::- TExp Int32 ->- TExp Int32 ->- TExp Int32 ->- TExp Int32 ->- ([TExp Int32], [TExp Int32])-lowDimKernelAndGroupSize block_dim num_arrays x_elems y_elems =- ( [ x_elems `divUp` block_dim,- y_elems `divUp` block_dim,- num_arrays- ],- [block_dim, block_dim, 1]- )
src/Futhark/Construct.hs view
@@ -348,12 +348,12 @@ _ -> error $ "eSignum: operand " ++ prettyString e ++ " has invalid type." --- | Construct a 'Copy' expression.+-- | Copy a value. eCopy :: MonadBuilder m => m (Exp (Rep m)) -> m (Exp (Rep m))-eCopy e = BasicOp . Copy <$> (letExp "copy_arg" =<< e)+eCopy e = BasicOp . Replicate mempty <$> (letSubExp "copy_arg" =<< e) -- | Construct a body from expressions. If multiple expressions are -- provided, their results will be concatenated in order and returned
src/Futhark/Doc/Generator.hs view
@@ -6,6 +6,7 @@ import Control.Monad import Control.Monad.Reader import Control.Monad.Writer (Writer, WriterT, runWriter, runWriterT, tell)+import Data.Bifunctor (second) import Data.Char (isAlpha, isSpace, toUpper) import Data.List (find, groupBy, inits, intersperse, isPrefixOf, partition, sort, sortOn, tails) import Data.Map qualified as M@@ -20,7 +21,7 @@ import Language.Futhark import Language.Futhark.Semantic import Language.Futhark.TypeChecker.Monad hiding (warn)-import System.FilePath (makeRelative, splitPath, (-<.>), (<.>), (</>))+import System.FilePath (makeRelative, splitPath, (-<.>), (</>)) import Text.Blaze.Html5 (AttributeValue, Html, toHtml, (!)) import Text.Blaze.Html5 qualified as H import Text.Blaze.Html5.Attributes qualified as A@@ -180,7 +181,7 @@ where file_map = vnameToFileMap imports importHtml import_name =- "doc" </> makeRelative "/" (fromString (includeToString import_name)) <.> "html"+ "doc" </> makeRelative "/" (fromString (includeToString import_name)) -<.> "html" -- | The header documentation (which need not be present) can contain -- an abstract and further sections.@@ -188,10 +189,10 @@ headerDoc prog = case progDoc prog of Just (DocComment doc loc) -> do- let (abstract, more_sections) = splitHeaderDoc doc+ let (abstract, more_sections) = splitHeaderDoc $ T.unpack doc first_paragraph <- docHtml $ Just $ DocComment (firstParagraph abstract) loc- abstract' <- docHtml $ Just $ DocComment abstract loc- more_sections' <- docHtml $ Just $ DocComment more_sections loc+ abstract' <- docHtml $ Just $ DocComment (T.pack abstract) loc+ more_sections' <- docHtml $ Just $ DocComment (T.pack more_sections) loc pure ( first_paragraph, selfLink "abstract" (H.h2 "Abstract") <> abstract',@@ -203,7 +204,7 @@ fromMaybe (s, mempty) $ find (("\n##" `isPrefixOf`) . snd) $ zip (inits s) (tails s)- firstParagraph = unlines . takeWhile (not . paragraphSeparator) . lines+ firstParagraph = T.pack . unlines . takeWhile (not . paragraphSeparator) . lines paragraphSeparator = all isSpace contentsPage :: [ImportName] -> [(ImportName, Html)] -> Html@@ -398,8 +399,10 @@ Just $ parens <$> me' synopsisOpened (ModImport _ (Info file) _) = Just $ do current <- asks ctxCurrent- let dest = fromString $ relativise (includeToFilePath file) current <> ".html"- pure $ keyword "import " <> (H.a ! A.href dest) (fromString $ show file)+ let dest = fromString $ relativise (includeToFilePath file) current -<.> "html"+ pure $+ keyword "import "+ <> (H.a ! A.href dest) (fromString (show (includeToString file))) synopsisOpened (ModAscript _ se _ _) = Just $ do se' <- synopsisSigExp se pure $ "... : " <> se'@@ -415,9 +418,7 @@ valBindHtml name (ValBind _ _ retdecl (Info rettype) tparams params _ _ _ _) = do let tparams' = mconcat $ map ((" " <>) . typeParamHtml) tparams noLink' =- noLink $- map typeParamName tparams- ++ map identName (S.toList $ mconcat $ map patIdents params)+ noLink $ map typeParamName tparams <> foldMap patNames params rettype' <- noLink' $ maybe (retTypeHtml rettype) typeExpHtml retdecl params' <- noLink' $ mapM paramHtml params pure@@ -479,13 +480,13 @@ renderTypeBind :: (VName, TypeBinding) -> DocM Html renderTypeBind (name, TypeAbbr l tps tp) = do- tp' <- retTypeHtml tp+ tp' <- retTypeHtml $ toResRet Nonunique tp pure $ H.div $ typeAbbrevHtml l (vnameHtml name) tps <> " = " <> tp' synopsisValBindBind :: (VName, BoundV) -> DocM Html synopsisValBindBind (name, BoundV tps t) = do let tps' = map typeParamHtml tps- t' <- typeHtml t+ t' <- typeHtml $ second (const Nonunique) t pure $ keyword "val " <> vnameHtml name@@ -497,11 +498,11 @@ dietHtml Consume = "*" dietHtml Observe = "" -typeHtml :: StructType -> DocM Html+typeHtml :: TypeBase Size Uniqueness -> DocM Html typeHtml t = case t of- Array _ u shape et -> do+ Array u shape et -> do shape' <- prettyShape shape- et' <- typeHtml $ Scalar et+ et' <- typeHtml $ Scalar $ second (const Nonunique) et pure $ prettyU u <> shape' <> et' Scalar (Prim et) -> pure $ primTypeHtml et Scalar (Record fs)@@ -513,12 +514,12 @@ ppField (name, tp) = do tp' <- typeHtml tp pure $ toHtml (nameToString name) <> ": " <> tp'- Scalar (TypeVar _ u et targs) -> do+ Scalar (TypeVar u et targs) -> do targs' <- mapM typeArgHtml targs et' <- qualNameHtml et pure $ prettyU u <> et' <> mconcat (map (" " <>) targs') Scalar (Arrow _ pname d t1 t2) -> do- t1' <- typeHtml t1+ t1' <- typeHtml $ second (const Nonunique) t1 t2' <- retTypeHtml t2 pure $ case pname of Named v ->@@ -530,7 +531,7 @@ ppClause (n, ts) = joinBy " " . (ppConstr n :) <$> mapM typeHtml ts ppConstr name = "#" <> toHtml (nameToString name) -retTypeHtml :: StructRetType -> DocM Html+retTypeHtml :: ResRetType -> DocM Html retTypeHtml (RetType [] t) = typeHtml t retTypeHtml (RetType dims t) = do t' <- typeHtml t@@ -541,8 +542,8 @@ mconcat <$> mapM dimDeclHtml ds typeArgHtml :: TypeArg Size -> DocM Html-typeArgHtml (TypeArgDim d _) = dimDeclHtml d-typeArgHtml (TypeArgType t _) = typeHtml t+typeArgHtml (TypeArgDim d) = dimDeclHtml d+typeArgHtml (TypeArgType t) = typeHtml $ second (const Nonunique) t modParamHtml :: [ModParamBase Info VName] -> DocM Html modParamHtml [] = pure mempty@@ -691,24 +692,22 @@ vnameLink' (VName _ tag) current file = if file == current then "#" ++ show tag- else relativise file current ++ ".html#" ++ show tag+ else relativise file current -<.> ".html#" ++ show tag -paramHtml :: Pat -> DocM Html+paramHtml :: Pat ParamType -> DocM Html paramHtml pat = do let (pat_param, d, t) = patternParam pat- t' <- typeHtml t+ t' <- typeHtml $ second (const Nonunique) t pure $ case pat_param of Named v -> parens (vnameHtml v <> ": " <> dietHtml d <> t') Unnamed -> t' relativise :: FilePath -> FilePath -> FilePath relativise dest src =- concat (replicate (length (splitPath src) - 1) "../") ++ dest+ concat (replicate (length (splitPath src) - 1) "../") ++ makeRelative "/" dest dimDeclHtml :: Size -> DocM Html-dimDeclHtml (NamedSize v) = brackets <$> qualNameHtml v-dimDeclHtml (ConstSize n) = pure $ brackets $ toHtml (show n)-dimDeclHtml AnySize {} = pure $ brackets mempty+dimDeclHtml = pure . brackets . toHtml . prettyString dimExpHtml :: SizeExp Info VName -> DocM Html dimExpHtml (SizeExpAny _) = pure $ brackets mempty@@ -735,7 +734,7 @@ H.preEscapedText . GFM.commonmarkToHtml [] [GFM.extAutolink] . T.pack- <$> identifierLinks loc doc+ <$> identifierLinks loc (T.unpack doc) docHtml Nothing = pure mempty identifierLinks :: SrcLoc -> String -> DocM String
src/Futhark/IR/GPUMem.hs view
@@ -44,6 +44,10 @@ opReturns (SegOp op) = segOpReturns op opReturns k = extReturns <$> opType k +instance OpReturns (HostOp NoOp (Aliases GPUMem)) where+ opReturns (SegOp op) = segOpReturns op+ opReturns k = extReturns <$> opType k+ instance OpReturns (HostOp NoOp (Engine.Wise GPUMem)) where opReturns (SegOp op) = segOpReturns op opReturns k = extReturns <$> opType k
src/Futhark/IR/MCMem.hs view
@@ -14,6 +14,7 @@ where import Futhark.Analysis.PrimExp.Convert+import Futhark.IR.Aliases (Aliases) import Futhark.IR.MC.Op import Futhark.IR.Mem import Futhark.IR.Mem.Simplify@@ -37,6 +38,10 @@ expTypesFromPat = pure . map snd . bodyReturnsFromPat instance OpReturns (MCOp NoOp MCMem) where+ opReturns (ParOp _ op) = segOpReturns op+ opReturns (OtherOp NoOp) = pure []++instance OpReturns (MCOp NoOp (Aliases MCMem)) where opReturns (ParOp _ op) = segOpReturns op opReturns (OtherOp NoOp) = pure []
src/Futhark/IR/Mem.hs view
@@ -279,6 +279,9 @@ declExtTypeOf (MemArray pt shape u _) = Array pt shape u declExtTypeOf (MemAcc acc ispace ts u) = Acc acc ispace ts u +instance FixExt ret => ExtTyped (MemInfo ExtSize Uniqueness ret) where+ extTypeOf = fromDecl . declExtTypeOf+ instance FixExt ret => ExtTyped (MemInfo ExtSize NoUniqueness ret) where extTypeOf (MemPrim pt) = Prim pt extTypeOf (MemMem space) = Mem space@@ -1070,7 +1073,7 @@ isMergeVar v = find ((== v) . paramName . snd) $ zip [0 ..] mergevars mergevars = map fst merge expReturns (Apply _ _ ret _) =- pure $ map funReturnsToExpReturns ret+ pure $ map (funReturnsToExpReturns . fst) ret expReturns (Match _ _ _ (MatchDec ret _)) = pure $ map bodyReturnsToExpReturns ret expReturns (Op op) =
src/Futhark/IR/Mem/IxFun.hs view
@@ -44,116 +44,35 @@ import Control.Monad import Control.Monad.State import Data.Function (on, (&))-import Data.List (elemIndex, partition, sort, sortBy, zip4, zipWith4)+import Data.List (sort, sortBy, zip4, zipWith4) import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE import Data.Map.Strict qualified as M-import Data.Maybe (fromJust, isJust, isNothing)+import Data.Maybe (isJust) import Data.Traversable-import Futhark.Analysis.AlgSimplify qualified as AlgSimplify import Futhark.Analysis.PrimExp import Futhark.Analysis.PrimExp.Convert-import Futhark.IR.Mem.Interval+import Futhark.IR.Mem.LMAD import Futhark.IR.Prop import Futhark.IR.Syntax ( DimIndex (..), FlatDimIndex (..), FlatSlice (..), Slice (..),- Type, dimFix, flatSliceDims, flatSliceStrides, unitSlice, )-import Futhark.IR.Syntax.Core (Ext (..), VName (..))+import Futhark.IR.Syntax.Core (Ext (..)) import Futhark.Transform.Rename import Futhark.Transform.Substitute-import Futhark.Util import Futhark.Util.IntegralExp import Futhark.Util.Pretty import Prelude hiding (gcd, id, mod, (.)) --- | The shape of an index function.-type Shape num = [num]- type Indices num = [num] -type Permutation = [Int]---- | The physical element ordering alongside a dimension, i.e. the--- sign of the stride.-data Monotonicity- = -- | Increasing.- Inc- | -- | Decreasing.- Dec- | -- | Unknown.- Unknown- deriving (Show, Eq)---- | A single dimension in an 'LMAD'.-data LMADDim num = LMADDim- { ldStride :: num,- ldShape :: num,- ldPerm :: Int,- ldMon :: Monotonicity- }- deriving (Show, Eq)--instance Ord Monotonicity where- (<=) _ Inc = True- (<=) Unknown _ = True- (<=) _ Unknown = False- (<=) Inc Dec = False- (<=) _ Dec = True--instance Ord num => Ord (LMADDim num) where- (LMADDim s1 q1 p1 m1) <= (LMADDim s2 q2 p2 m2) =- ([q1, s1] < [q2, s2])- || ( ([q1, s1] == [q2, s2])- && ( (p1 < p2)- || ( (p1 == p2)- && (m1 <= m2)- )- )- )---- | LMAD's representation consists of a general offset and for each dimension a--- stride, number of elements (or shape), permutation, and--- monotonicity. Note that the permutation is not strictly necessary in that the--- permutation can be performed directly on LMAD dimensions, but then it is--- difficult to extract the permutation back from an LMAD.------ LMAD algebra is closed under composition w.r.t. operators such as--- permute, index and slice. However, other operations, such as--- reshape, cannot always be represented inside the LMAD algebra.------ It follows that the general representation of an index function is a list of--- LMADS, in which each following LMAD in the list implicitly corresponds to an--- irregular reshaping operation.------ However, we expect that the common case is when the index function is one--- LMAD -- we call this the "nice" representation.------ Finally, the list of LMADs is kept in an @IxFun@ together with the shape of--- the original array, and a bit to indicate whether the index function is--- contiguous, i.e., if we instantiate all the points of the current index--- function, do we get a contiguous memory interval?------ By definition, the LMAD \( \sigma + \{ (n_1, s_1), \ldots, (n_k, s_k) \} \),--- where \(n\) and \(s\) denote the shape and stride of each dimension, denotes--- the set of points:------ \[--- \{ ~ \sigma + i_1 * s_1 + \ldots + i_m * s_m ~ | ~ 0 \leq i_1 < n_1, \ldots, 0 \leq i_m < n_m ~ \}--- \]-data LMAD num = LMAD- { lmadOffset :: num,- lmadDims :: [LMADDim num]- }- deriving (Show, Eq, Ord)- -- | An index function is a mapping from a multidimensional array -- index space (the domain) to a one-dimensional memory index space. -- Essentially, it explains where the element at position @[i,j,p]@ of@@ -172,21 +91,6 @@ } deriving (Show, Eq) -instance Pretty Monotonicity where- pretty = pretty . show--instance Pretty num => Pretty (LMAD num) where- pretty (LMAD offset dims) =- braces . semistack $- [ "offset:" <+> group (pretty offset),- "strides:" <+> p ldStride,- "shape:" <+> p ldShape,- "permutation:" <+> p ldPerm,- "monotonicity:" <+> p ldMon- ]- where- p f = group $ brackets $ align $ commasep $ map (pretty . f) dims- instance Pretty num => Pretty (IxFun num) where pretty (IxFun lmads oshp cg) = braces . semistack $@@ -195,45 +99,21 @@ "LMADs:" <+> brackets (commastack $ NE.toList $ NE.map pretty lmads) ] -instance Substitute num => Substitute (LMAD num) where- substituteNames substs = fmap $ substituteNames substs- instance Substitute num => Substitute (IxFun num) where substituteNames substs = fmap $ substituteNames substs -instance Substitute num => Rename (LMAD num) where- rename = substituteRename- instance Substitute num => Rename (IxFun num) where rename = substituteRename -instance FreeIn num => FreeIn (LMAD num) where- freeIn' = foldMap freeIn'- instance FreeIn num => FreeIn (IxFun num) where freeIn' = foldMap freeIn' -instance FreeIn num => FreeIn (LMADDim num) where- freeIn' (LMADDim s n _ _) = freeIn' s <> freeIn' n--instance Functor LMAD where- fmap = fmapDefault- instance Functor IxFun where fmap = fmapDefault -instance Foldable LMAD where- foldMap = foldMapDefault- instance Foldable IxFun where foldMap = foldMapDefault -instance Traversable LMAD where- traverse f (LMAD offset dims) =- LMAD <$> f offset <*> traverse f' dims- where- f' (LMADDim s n p m) = LMADDim <$> f s <*> f n <*> pure p <*> pure m- -- It is important that the traversal order here is the same as in -- mkExistential. instance Traversable IxFun where@@ -248,14 +128,6 @@ (@++@) :: NonEmpty a -> NonEmpty a -> NonEmpty a (x :| xs) @++@ (y :| ys) = x :| xs ++ [y] ++ ys -invertMonotonicity :: Monotonicity -> Monotonicity-invertMonotonicity Inc = Dec-invertMonotonicity Dec = Inc-invertMonotonicity Unknown = Unknown--lmadPermutation :: LMAD num -> Permutation-lmadPermutation = map ldPerm . lmadDims- setLMADPermutation :: Permutation -> LMAD num -> LMAD num setLMADPermutation perm lmad = lmad {lmadDims = zipWith (\dim p -> dim {ldPerm = p}) (lmadDims lmad) perm}@@ -263,29 +135,6 @@ setLMADShape :: Shape num -> LMAD num -> LMAD num setLMADShape shp lmad = lmad {lmadDims = zipWith (\dim s -> dim {ldShape = s}) (lmadDims lmad) shp} --- | Substitute a name with a PrimExp in an LMAD.-substituteInLMAD ::- Ord a =>- M.Map a (TPrimExp t a) ->- LMAD (TPrimExp t a) ->- LMAD (TPrimExp t a)-substituteInLMAD tab (LMAD offset dims) =- let offset' = sub offset- dims' =- map- ( \(LMADDim s n p m) ->- LMADDim- (sub s)- (sub n)- p- m- )- dims- in LMAD offset' dims'- where- tab' = fmap untyped tab- sub = TPrimExp . substituteInPrimExp tab' . untyped- -- | Substitute a name with a PrimExp in an index function. substituteInIxFun :: Ord a =>@@ -330,14 +179,6 @@ shape (IxFun (lmad :| _) _ _) = permuteFwd (lmadPermutation lmad) $ lmadShapeBase lmad --- | Shape of an LMAD.-lmadShape :: (Eq num, IntegralExp num) => LMAD num -> Shape num-lmadShape lmad = permuteInv (lmadPermutation lmad) $ lmadShapeBase lmad---- | Shape of an LMAD, ignoring permutations.-lmadShapeBase :: (Eq num, IntegralExp num) => LMAD num -> Shape num-lmadShapeBase = map ldShape . lmadDims- -- | Compute the flat memory index for a complete set @inds@ of array indices -- and a certain element size @elem_size@. index ::@@ -821,12 +662,6 @@ isLinear :: (Eq num, IntegralExp num) => IxFun num -> Bool isLinear = (== Just 0) . flip linearWithOffset 1 -permuteFwd :: Permutation -> [a] -> [a]-permuteFwd ps elems = map (elems !!) ps--permuteInv :: Permutation -> [a] -> [a]-permuteInv ps elems = map snd $ sortBy (compare `on` fst) $ zip ps elems- flatOneDim :: (Eq num, IntegralExp num) => num ->@@ -836,28 +671,6 @@ | s == 0 = 0 | otherwise = i * s --- | Generalised iota with user-specified offset and rotates.-makeRotIota ::- IntegralExp num =>- Monotonicity ->- -- | Offset- num ->- -- | Shape- [num] ->- LMAD num-makeRotIota mon off ns- | mon == Inc || mon == Dec =- let rk = length ns- ss0 = reverse $ take rk $ scanl (*) 1 $ reverse ns- ss =- if mon == Inc- then ss0- else map (* (-1)) ss0- ps = map fromIntegral [0 .. rk - 1]- fi = replicate rk mon- in LMAD off $ zipWith4 LMADDim ss ns ps fi- | otherwise = error "makeRotIota: requires Inc or Dec"- -- | Check monotonicity of an index function. ixfunMonotonicity :: (Eq num, IntegralExp num) =>@@ -940,245 +753,3 @@ == lmadOffset lmad2 && map ldStride (lmadDims lmad1) == map ldStride (lmadDims lmad2)---- | Computes the maximum span of an 'LMAD'. The result is the lowest and--- highest flat values representable by that 'LMAD'.-flatSpan :: LMAD (TPrimExp Int64 VName) -> TPrimExp Int64 VName-flatSpan (LMAD _ dims) =- foldr- ( \dim upper ->- let spn = ldStride dim * (ldShape dim - 1)- in -- If you've gotten this far, you've already lost- spn + upper- )- 0- dims---- | Conservatively flatten a list of LMAD dimensions------ Since not all LMADs can actually be flattened, we try to overestimate the--- flattened array instead. This means that any "holes" in betwen dimensions--- will get filled out.--- conservativeFlatten :: (IntegralExp e, Ord e, Pretty e) => LMAD e -> LMAD e-conservativeFlatten :: LMAD (TPrimExp Int64 VName) -> Maybe (LMAD (TPrimExp Int64 VName))-conservativeFlatten (LMAD offset []) =- pure $ LMAD offset [LMADDim 1 1 0 Inc]-conservativeFlatten l@(LMAD _ [_]) =- pure l-conservativeFlatten l@(LMAD offset dims) = do- strd <-- foldM- gcd- (ldStride $ head dims)- $ map ldStride dims- pure $ LMAD offset [LMADDim strd (shp + 1) 0 Unknown]- where- shp = flatSpan l---- | Very conservative GCD calculation. Returns 'Nothing' if the result cannot--- be immediately determined. Does not recurse at all.-gcd :: TPrimExp Int64 VName -> TPrimExp Int64 VName -> Maybe (TPrimExp Int64 VName)-gcd x y = gcd' (abs x) (abs y)- where- gcd' a b | a == b = Just a- gcd' 1 _ = Just 1- gcd' _ 1 = Just 1- gcd' a 0 = Just a- gcd' _ _ = Nothing -- gcd' b (a `Futhark.Util.IntegralExp.rem` b)---- | Returns @True@ if the two 'LMAD's could be proven disjoint.------ Uses some best-approximation heuristics to determine disjointness. For two--- 1-dimensional arrays, we can guarantee whether or not they are disjoint, but--- as soon as more than one dimension is involved, things get more--- tricky. Currently, we try to 'conservativelyFlatten' any LMAD with more than--- one dimension.-disjoint :: [(VName, PrimExp VName)] -> Names -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool-disjoint less_thans non_negatives (LMAD offset1 [dim1]) (LMAD offset2 [dim2]) =- doesNotDivide (gcd (ldStride dim1) (ldStride dim2)) (offset1 - offset2)- || AlgSimplify.lessThanish- less_thans- non_negatives- (offset2 + (ldShape dim2 - 1) * ldStride dim2)- offset1- || AlgSimplify.lessThanish- less_thans- non_negatives- (offset1 + (ldShape dim1 - 1) * ldStride dim1)- offset2- where- doesNotDivide :: Maybe (TPrimExp Int64 VName) -> TPrimExp Int64 VName -> Bool- doesNotDivide (Just x) y =- Futhark.Util.IntegralExp.mod y x- & untyped- & constFoldPrimExp- & TPrimExp- & (.==.) (0 :: TPrimExp Int64 VName)- & primBool- & maybe False not- doesNotDivide _ _ = False-disjoint less_thans non_negatives lmad1 lmad2 =- case (conservativeFlatten lmad1, conservativeFlatten lmad2) of- (Just lmad1', Just lmad2') -> disjoint less_thans non_negatives lmad1' lmad2'- _ -> False--disjoint2 :: scope -> asserts -> [(VName, PrimExp VName)] -> Names -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool-disjoint2 _ _ less_thans non_negatives lmad1 lmad2 =- let (offset1, interval1) = lmadToIntervals lmad1- (offset2, interval2) = lmadToIntervals lmad2- (neg_offset, pos_offset) =- partition AlgSimplify.negated $- offset1 `AlgSimplify.sub` offset2- (interval1', interval2') =- unzip $- sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride . fst)) $- intervalPairs interval1 interval2- in case ( distributeOffset pos_offset interval1',- distributeOffset (map AlgSimplify.negate neg_offset) interval2'- ) of- (Just interval1'', Just interval2'') ->- isNothing- ( selfOverlap () () less_thans (map (flip LeafExp $ IntType Int64) $ namesToList non_negatives) interval1''- )- && isNothing- ( selfOverlap () () less_thans (map (flip LeafExp $ IntType Int64) $ namesToList non_negatives) interval2''- )- && not- ( all- (uncurry (intervalOverlap less_thans non_negatives))- (zip interval1'' interval2'')- )- _ ->- False--disjoint3 :: M.Map VName Type -> [PrimExp VName] -> [(VName, PrimExp VName)] -> [PrimExp VName] -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool-disjoint3 scope asserts less_thans non_negatives lmad1 lmad2 =- let (offset1, interval1) = lmadToIntervals lmad1- (offset2, interval2) = lmadToIntervals lmad2- interval1' = fixPoint (mergeDims . joinDims) $ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride)) interval1- interval2' = fixPoint (mergeDims . joinDims) $ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride)) interval2- (interval1'', interval2'') =- unzip $- sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride . fst)) $- intervalPairs interval1' interval2'- in disjointHelper 4 interval1'' interval2'' $ offset1 `AlgSimplify.sub` offset2- where- disjointHelper :: Int -> [Interval] -> [Interval] -> AlgSimplify.SofP -> Bool- disjointHelper 0 _ _ _ = False- disjointHelper i is10 is20 offset =- let (is1, is2) =- unzip $- sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride . fst)) $- intervalPairs is10 is20- (neg_offset, pos_offset) = partition AlgSimplify.negated offset- in case ( distributeOffset pos_offset is1,- distributeOffset (map AlgSimplify.negate neg_offset) is2- ) of- (Just is1', Just is2') -> do- let overlap1 = selfOverlap scope asserts less_thans non_negatives is1'- let overlap2 = selfOverlap scope asserts less_thans non_negatives is2'- case (overlap1, overlap2) of- (Nothing, Nothing) ->- case namesFromList <$> mapM justLeafExp non_negatives of- Just non_negatives' ->- not $- all- (uncurry (intervalOverlap less_thans non_negatives'))- (zip is1 is2)- _ -> False- (Just overlapping_dim, _) ->- let expanded_offset = AlgSimplify.simplifySofP' <$> expandOffset offset is1- splits = splitDim overlapping_dim is1'- in all (\(new_offset, new_is1) -> disjointHelper (i - 1) (joinDims new_is1) (joinDims is2') new_offset) splits- || maybe False (disjointHelper (i - 1) is1 is2) expanded_offset- (_, Just overlapping_dim) ->- let expanded_offset = AlgSimplify.simplifySofP' <$> expandOffset offset is2- splits = splitDim overlapping_dim is2'- in all- ( \(new_offset, new_is2) ->- disjointHelper (i - 1) (joinDims is1') (joinDims new_is2) $- map AlgSimplify.negate new_offset- )- splits- || maybe False (disjointHelper (i - 1) is1 is2) expanded_offset- _ -> False--joinDims :: [Interval] -> [Interval]-joinDims = helper []- where- helper acc [] = reverse acc- helper acc [x] = reverse $ x : acc- helper acc (x : y : rest) =- if stride x == stride y && lowerBound x == 0 && lowerBound y == 0- then helper acc $ x {numElements = numElements x * numElements y} : rest- else helper (x : acc) (y : rest)--mergeDims :: [Interval] -> [Interval]-mergeDims = helper [] . reverse- where- helper acc [] = acc- helper acc [x] = x : acc- helper acc (x : y : rest) =- if stride x * numElements x == stride y && lowerBound x == 0 && lowerBound y == 0- then helper acc $ x {numElements = numElements x * numElements y} : rest- else helper (x : acc) (y : rest)--splitDim :: Interval -> [Interval] -> [(AlgSimplify.SofP, [Interval])]-splitDim overlapping_dim0 is- | [st] <- AlgSimplify.simplify0 $ untyped $ stride overlapping_dim0,- [st1] <- AlgSimplify.simplify0 $ untyped $ stride overlapping_dim,- [spn] <- AlgSimplify.simplify0 $ untyped $ stride overlapping_dim * numElements overlapping_dim,- lowerBound overlapping_dim == 0,- Just big_dim_elems <- AlgSimplify.maybeDivide spn st,- Just small_dim_elems <- AlgSimplify.maybeDivide st st1 =- [ ( [],- init before- <> [ Interval 0 (isInt64 $ AlgSimplify.prodToExp big_dim_elems) (stride overlapping_dim0),- Interval 0 (isInt64 $ AlgSimplify.prodToExp small_dim_elems) (stride overlapping_dim)- ]- <> after- )- ]- | otherwise =- let shrunk_dim = overlapping_dim {numElements = numElements overlapping_dim - 1}- point_offset = AlgSimplify.simplify0 $ untyped $ (numElements overlapping_dim - 1 + lowerBound overlapping_dim) * stride overlapping_dim- in [ (point_offset, before <> after),- ([], before <> [shrunk_dim] <> after)- ]- where- (before, overlapping_dim, after) =- fromJust $- elemIndex overlapping_dim0 is- >>= (flip focusNth is . (+ 1))--lmadToIntervals :: LMAD (TPrimExp Int64 VName) -> (AlgSimplify.SofP, [Interval])-lmadToIntervals (LMAD offset []) = (AlgSimplify.simplify0 $ untyped offset, [Interval 0 1 1])-lmadToIntervals lmad@(LMAD offset dims0) =- (offset', map helper $ permuteInv (lmadPermutation lmad) dims0)- where- offset' = AlgSimplify.simplify0 $ untyped offset-- helper :: LMADDim (TPrimExp Int64 VName) -> Interval- helper (LMADDim strd shp _ _) = do- Interval 0 (AlgSimplify.simplify' shp) (AlgSimplify.simplify' strd)---- | Dynamically determine if two 'LMADDim' are equal.------ True if the dynamic values of their constituents are equal.-dynamicEqualsLMADDim :: Eq num => LMADDim (TPrimExp t num) -> LMADDim (TPrimExp t num) -> TPrimExp Bool num-dynamicEqualsLMADDim dim1 dim2 =- ldStride dim1 .==. ldStride dim2- .&&. ldShape dim1 .==. ldShape dim2- .&&. fromBool (ldPerm dim1 == ldPerm dim2)- .&&. fromBool (ldMon dim1 == ldMon dim2)---- | Dynamically determine if two 'LMAD' are equal.------ True if offset and constituent 'LMADDim' are equal.-dynamicEqualsLMAD :: Eq num => LMAD (TPrimExp t num) -> LMAD (TPrimExp t num) -> TPrimExp Bool num-dynamicEqualsLMAD lmad1 lmad2 =- lmadOffset lmad1 .==. lmadOffset lmad2- .&&. foldr- ((.&&.) . uncurry dynamicEqualsLMADDim)- true- (zip (lmadDims lmad1) (lmadDims lmad2))
+ src/Futhark/IR/Mem/LMAD.hs view
@@ -0,0 +1,470 @@+-- | This module contains a representation of linear-memory accessor+-- descriptors (LMAD); see work by Zhu, Hoeflinger and David.+module Futhark.IR.Mem.LMAD+ ( Shape,+ LMAD (..),+ LMADDim (..),+ Monotonicity (..),+ Permutation,+ lmadShape,+ lmadShapeBase,+ substituteInLMAD,+ permuteInv,+ permuteFwd,+ conservativeFlatten,+ disjoint,+ disjoint2,+ disjoint3,+ dynamicEqualsLMAD,+ lmadPermutation,+ makeRotIota,+ invertMonotonicity,+ )+where++import Control.Category+import Control.Monad+import Data.Function (on, (&))+import Data.List (elemIndex, partition, sortBy, zipWith4)+import Data.Map.Strict qualified as M+import Data.Maybe (fromJust, isNothing)+import Data.Traversable+import Futhark.Analysis.AlgSimplify qualified as AlgSimplify+import Futhark.Analysis.PrimExp+import Futhark.Analysis.PrimExp.Convert+import Futhark.IR.Mem.Interval+import Futhark.IR.Prop+import Futhark.IR.Syntax (Type)+import Futhark.IR.Syntax.Core (VName (..))+import Futhark.Transform.Rename+import Futhark.Transform.Substitute+import Futhark.Util+import Futhark.Util.IntegralExp+import Futhark.Util.Pretty+import Prelude hiding (gcd, id, mod, (.))++-- | The shape of an index function.+type Shape num = [num]++type Permutation = [Int]++-- | The physical element ordering alongside a dimension, i.e. the+-- sign of the stride.+data Monotonicity+ = -- | Increasing.+ Inc+ | -- | Decreasing.+ Dec+ | -- | Unknown.+ Unknown+ deriving (Show, Eq)++-- | A single dimension in an 'LMAD'.+data LMADDim num = LMADDim+ { ldStride :: num,+ ldShape :: num,+ ldPerm :: Int,+ ldMon :: Monotonicity+ }+ deriving (Show, Eq)++instance Ord Monotonicity where+ (<=) _ Inc = True+ (<=) Unknown _ = True+ (<=) _ Unknown = False+ (<=) Inc Dec = False+ (<=) _ Dec = True++instance Ord num => Ord (LMADDim num) where+ (LMADDim s1 q1 p1 m1) <= (LMADDim s2 q2 p2 m2) =+ ([q1, s1] < [q2, s2])+ || ( ([q1, s1] == [q2, s2])+ && ( (p1 < p2)+ || ( (p1 == p2)+ && (m1 <= m2)+ )+ )+ )++-- | LMAD's representation consists of a general offset and for each dimension a+-- stride, number of elements (or shape), permutation, and+-- monotonicity. Note that the permutation is not strictly necessary in that the+-- permutation can be performed directly on LMAD dimensions, but then it is+-- difficult to extract the permutation back from an LMAD.+--+-- LMAD algebra is closed under composition w.r.t. operators such as+-- permute, index and slice. However, other operations, such as+-- reshape, cannot always be represented inside the LMAD algebra.+--+-- It follows that the general representation of an index function is a list of+-- LMADS, in which each following LMAD in the list implicitly corresponds to an+-- irregular reshaping operation.+--+-- However, we expect that the common case is when the index function is one+-- LMAD -- we call this the "nice" representation.+--+-- Finally, the list of LMADs is kept in an @IxFun@ together with the shape of+-- the original array, and a bit to indicate whether the index function is+-- contiguous, i.e., if we instantiate all the points of the current index+-- function, do we get a contiguous memory interval?+--+-- By definition, the LMAD \( \sigma + \{ (n_1, s_1), \ldots, (n_k, s_k) \} \),+-- where \(n\) and \(s\) denote the shape and stride of each dimension, denotes+-- the set of points:+--+-- \[+-- \{ ~ \sigma + i_1 * s_1 + \ldots + i_m * s_m ~ | ~ 0 \leq i_1 < n_1, \ldots, 0 \leq i_m < n_m ~ \}+-- \]+data LMAD num = LMAD+ { lmadOffset :: num,+ lmadDims :: [LMADDim num]+ }+ deriving (Show, Eq, Ord)++instance Pretty Monotonicity where+ pretty = pretty . show++instance Pretty num => Pretty (LMAD num) where+ pretty (LMAD offset dims) =+ braces . semistack $+ [ "offset:" <+> group (pretty offset),+ "strides:" <+> p ldStride,+ "shape:" <+> p ldShape,+ "permutation:" <+> p ldPerm,+ "monotonicity:" <+> p ldMon+ ]+ where+ p f = group $ brackets $ align $ commasep $ map (pretty . f) dims++instance Substitute num => Substitute (LMAD num) where+ substituteNames substs = fmap $ substituteNames substs++instance Substitute num => Rename (LMAD num) where+ rename = substituteRename++instance FreeIn num => FreeIn (LMAD num) where+ freeIn' = foldMap freeIn'++instance FreeIn num => FreeIn (LMADDim num) where+ freeIn' (LMADDim s n _ _) = freeIn' s <> freeIn' n++instance Functor LMAD where+ fmap = fmapDefault++instance Foldable LMAD where+ foldMap = foldMapDefault++instance Traversable LMAD where+ traverse f (LMAD offset dims) =+ LMAD <$> f offset <*> traverse f' dims+ where+ f' (LMADDim s n p m) = LMADDim <$> f s <*> f n <*> pure p <*> pure m++invertMonotonicity :: Monotonicity -> Monotonicity+invertMonotonicity Inc = Dec+invertMonotonicity Dec = Inc+invertMonotonicity Unknown = Unknown++lmadPermutation :: LMAD num -> Permutation+lmadPermutation = map ldPerm . lmadDims++-- | Substitute a name with a PrimExp in an LMAD.+substituteInLMAD ::+ Ord a =>+ M.Map a (TPrimExp t a) ->+ LMAD (TPrimExp t a) ->+ LMAD (TPrimExp t a)+substituteInLMAD tab (LMAD offset dims) =+ let offset' = sub offset+ dims' =+ map+ ( \(LMADDim s n p m) ->+ LMADDim+ (sub s)+ (sub n)+ p+ m+ )+ dims+ in LMAD offset' dims'+ where+ tab' = fmap untyped tab+ sub = TPrimExp . substituteInPrimExp tab' . untyped++-- | Shape of an LMAD.+lmadShape :: LMAD num -> Shape num+lmadShape lmad = permuteInv (lmadPermutation lmad) $ lmadShapeBase lmad++-- | Shape of an LMAD, ignoring permutations.+lmadShapeBase :: LMAD num -> Shape num+lmadShapeBase = map ldShape . lmadDims++permuteFwd :: Permutation -> [a] -> [a]+permuteFwd ps elems = map (elems !!) ps++permuteInv :: Permutation -> [a] -> [a]+permuteInv ps elems = map snd $ sortBy (compare `on` fst) $ zip ps elems++-- | Generalised iota with user-specified offset and rotates.+makeRotIota ::+ IntegralExp num =>+ Monotonicity ->+ -- | Offset+ num ->+ -- | Shape+ [num] ->+ LMAD num+makeRotIota mon off ns+ | mon == Inc || mon == Dec =+ let rk = length ns+ ss0 = reverse $ take rk $ scanl (*) 1 $ reverse ns+ ss =+ if mon == Inc+ then ss0+ else map (* (-1)) ss0+ ps = map fromIntegral [0 .. rk - 1]+ fi = replicate rk mon+ in LMAD off $ zipWith4 LMADDim ss ns ps fi+ | otherwise = error "makeRotIota: requires Inc or Dec"++-- | Computes the maximum span of an 'LMAD'. The result is the lowest and+-- highest flat values representable by that 'LMAD'.+flatSpan :: LMAD (TPrimExp Int64 VName) -> TPrimExp Int64 VName+flatSpan (LMAD _ dims) =+ foldr+ ( \dim upper ->+ let spn = ldStride dim * (ldShape dim - 1)+ in -- If you've gotten this far, you've already lost+ spn + upper+ )+ 0+ dims++-- | Conservatively flatten a list of LMAD dimensions+--+-- Since not all LMADs can actually be flattened, we try to overestimate the+-- flattened array instead. This means that any "holes" in betwen dimensions+-- will get filled out.+-- conservativeFlatten :: (IntegralExp e, Ord e, Pretty e) => LMAD e -> LMAD e+conservativeFlatten :: LMAD (TPrimExp Int64 VName) -> Maybe (LMAD (TPrimExp Int64 VName))+conservativeFlatten (LMAD offset []) =+ pure $ LMAD offset [LMADDim 1 1 0 Inc]+conservativeFlatten l@(LMAD _ [_]) =+ pure l+conservativeFlatten l@(LMAD offset dims) = do+ strd <-+ foldM+ gcd+ (ldStride $ head dims)+ $ map ldStride dims+ pure $ LMAD offset [LMADDim strd (shp + 1) 0 Unknown]+ where+ shp = flatSpan l++-- | Very conservative GCD calculation. Returns 'Nothing' if the result cannot+-- be immediately determined. Does not recurse at all.+gcd :: TPrimExp Int64 VName -> TPrimExp Int64 VName -> Maybe (TPrimExp Int64 VName)+gcd x y = gcd' (abs x) (abs y)+ where+ gcd' a b | a == b = Just a+ gcd' 1 _ = Just 1+ gcd' _ 1 = Just 1+ gcd' a 0 = Just a+ gcd' _ _ = Nothing -- gcd' b (a `Futhark.Util.IntegralExp.rem` b)++-- | Returns @True@ if the two 'LMAD's could be proven disjoint.+--+-- Uses some best-approximation heuristics to determine disjointness. For two+-- 1-dimensional arrays, we can guarantee whether or not they are disjoint, but+-- as soon as more than one dimension is involved, things get more+-- tricky. Currently, we try to 'conservativelyFlatten' any LMAD with more than+-- one dimension.+disjoint :: [(VName, PrimExp VName)] -> Names -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool+disjoint less_thans non_negatives (LMAD offset1 [dim1]) (LMAD offset2 [dim2]) =+ doesNotDivide (gcd (ldStride dim1) (ldStride dim2)) (offset1 - offset2)+ || AlgSimplify.lessThanish+ less_thans+ non_negatives+ (offset2 + (ldShape dim2 - 1) * ldStride dim2)+ offset1+ || AlgSimplify.lessThanish+ less_thans+ non_negatives+ (offset1 + (ldShape dim1 - 1) * ldStride dim1)+ offset2+ where+ doesNotDivide :: Maybe (TPrimExp Int64 VName) -> TPrimExp Int64 VName -> Bool+ doesNotDivide (Just x) y =+ Futhark.Util.IntegralExp.mod y x+ & untyped+ & constFoldPrimExp+ & TPrimExp+ & (.==.) (0 :: TPrimExp Int64 VName)+ & primBool+ & maybe False not+ doesNotDivide _ _ = False+disjoint less_thans non_negatives lmad1 lmad2 =+ case (conservativeFlatten lmad1, conservativeFlatten lmad2) of+ (Just lmad1', Just lmad2') -> disjoint less_thans non_negatives lmad1' lmad2'+ _ -> False++disjoint2 :: scope -> asserts -> [(VName, PrimExp VName)] -> Names -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool+disjoint2 _ _ less_thans non_negatives lmad1 lmad2 =+ let (offset1, interval1) = lmadToIntervals lmad1+ (offset2, interval2) = lmadToIntervals lmad2+ (neg_offset, pos_offset) =+ partition AlgSimplify.negated $+ offset1 `AlgSimplify.sub` offset2+ (interval1', interval2') =+ unzip $+ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride . fst)) $+ intervalPairs interval1 interval2+ in case ( distributeOffset pos_offset interval1',+ distributeOffset (map AlgSimplify.negate neg_offset) interval2'+ ) of+ (Just interval1'', Just interval2'') ->+ isNothing+ ( selfOverlap () () less_thans (map (flip LeafExp $ IntType Int64) $ namesToList non_negatives) interval1''+ )+ && isNothing+ ( selfOverlap () () less_thans (map (flip LeafExp $ IntType Int64) $ namesToList non_negatives) interval2''+ )+ && not+ ( all+ (uncurry (intervalOverlap less_thans non_negatives))+ (zip interval1'' interval2'')+ )+ _ ->+ False++disjoint3 :: M.Map VName Type -> [PrimExp VName] -> [(VName, PrimExp VName)] -> [PrimExp VName] -> LMAD (TPrimExp Int64 VName) -> LMAD (TPrimExp Int64 VName) -> Bool+disjoint3 scope asserts less_thans non_negatives lmad1 lmad2 =+ let (offset1, interval1) = lmadToIntervals lmad1+ (offset2, interval2) = lmadToIntervals lmad2+ interval1' = fixPoint (mergeDims . joinDims) $ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride)) interval1+ interval2' = fixPoint (mergeDims . joinDims) $ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride)) interval2+ (interval1'', interval2'') =+ unzip $+ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride . fst)) $+ intervalPairs interval1' interval2'+ in disjointHelper 4 interval1'' interval2'' $ offset1 `AlgSimplify.sub` offset2+ where+ disjointHelper :: Int -> [Interval] -> [Interval] -> AlgSimplify.SofP -> Bool+ disjointHelper 0 _ _ _ = False+ disjointHelper i is10 is20 offset =+ let (is1, is2) =+ unzip $+ sortBy (flip AlgSimplify.compareComplexity `on` (AlgSimplify.simplify0 . untyped . stride . fst)) $+ intervalPairs is10 is20+ (neg_offset, pos_offset) = partition AlgSimplify.negated offset+ in case ( distributeOffset pos_offset is1,+ distributeOffset (map AlgSimplify.negate neg_offset) is2+ ) of+ (Just is1', Just is2') -> do+ let overlap1 = selfOverlap scope asserts less_thans non_negatives is1'+ let overlap2 = selfOverlap scope asserts less_thans non_negatives is2'+ case (overlap1, overlap2) of+ (Nothing, Nothing) ->+ case namesFromList <$> mapM justLeafExp non_negatives of+ Just non_negatives' ->+ not $+ all+ (uncurry (intervalOverlap less_thans non_negatives'))+ (zip is1 is2)+ _ -> False+ (Just overlapping_dim, _) ->+ let expanded_offset = AlgSimplify.simplifySofP' <$> expandOffset offset is1+ splits = splitDim overlapping_dim is1'+ in all (\(new_offset, new_is1) -> disjointHelper (i - 1) (joinDims new_is1) (joinDims is2') new_offset) splits+ || maybe False (disjointHelper (i - 1) is1 is2) expanded_offset+ (_, Just overlapping_dim) ->+ let expanded_offset = AlgSimplify.simplifySofP' <$> expandOffset offset is2+ splits = splitDim overlapping_dim is2'+ in all+ ( \(new_offset, new_is2) ->+ disjointHelper (i - 1) (joinDims is1') (joinDims new_is2) $+ map AlgSimplify.negate new_offset+ )+ splits+ || maybe False (disjointHelper (i - 1) is1 is2) expanded_offset+ _ -> False++joinDims :: [Interval] -> [Interval]+joinDims = helper []+ where+ helper acc [] = reverse acc+ helper acc [x] = reverse $ x : acc+ helper acc (x : y : rest) =+ if stride x == stride y && lowerBound x == 0 && lowerBound y == 0+ then helper acc $ x {numElements = numElements x * numElements y} : rest+ else helper (x : acc) (y : rest)++mergeDims :: [Interval] -> [Interval]+mergeDims = helper [] . reverse+ where+ helper acc [] = acc+ helper acc [x] = x : acc+ helper acc (x : y : rest) =+ if stride x * numElements x == stride y && lowerBound x == 0 && lowerBound y == 0+ then helper acc $ x {numElements = numElements x * numElements y} : rest+ else helper (x : acc) (y : rest)++splitDim :: Interval -> [Interval] -> [(AlgSimplify.SofP, [Interval])]+splitDim overlapping_dim0 is+ | [st] <- AlgSimplify.simplify0 $ untyped $ stride overlapping_dim0,+ [st1] <- AlgSimplify.simplify0 $ untyped $ stride overlapping_dim,+ [spn] <- AlgSimplify.simplify0 $ untyped $ stride overlapping_dim * numElements overlapping_dim,+ lowerBound overlapping_dim == 0,+ Just big_dim_elems <- AlgSimplify.maybeDivide spn st,+ Just small_dim_elems <- AlgSimplify.maybeDivide st st1 =+ [ ( [],+ init before+ <> [ Interval 0 (isInt64 $ AlgSimplify.prodToExp big_dim_elems) (stride overlapping_dim0),+ Interval 0 (isInt64 $ AlgSimplify.prodToExp small_dim_elems) (stride overlapping_dim)+ ]+ <> after+ )+ ]+ | otherwise =+ let shrunk_dim = overlapping_dim {numElements = numElements overlapping_dim - 1}+ point_offset = AlgSimplify.simplify0 $ untyped $ (numElements overlapping_dim - 1 + lowerBound overlapping_dim) * stride overlapping_dim+ in [ (point_offset, before <> after),+ ([], before <> [shrunk_dim] <> after)+ ]+ where+ (before, overlapping_dim, after) =+ fromJust $+ elemIndex overlapping_dim0 is+ >>= (flip focusNth is . (+ 1))++lmadToIntervals :: LMAD (TPrimExp Int64 VName) -> (AlgSimplify.SofP, [Interval])+lmadToIntervals (LMAD offset []) = (AlgSimplify.simplify0 $ untyped offset, [Interval 0 1 1])+lmadToIntervals lmad@(LMAD offset dims0) =+ (offset', map helper $ permuteInv (lmadPermutation lmad) dims0)+ where+ offset' = AlgSimplify.simplify0 $ untyped offset++ helper :: LMADDim (TPrimExp Int64 VName) -> Interval+ helper (LMADDim strd shp _ _) = do+ Interval 0 (AlgSimplify.simplify' shp) (AlgSimplify.simplify' strd)++-- | Dynamically determine if two 'LMADDim' are equal.+--+-- True if the dynamic values of their constituents are equal.+dynamicEqualsLMADDim :: Eq num => LMADDim (TPrimExp t num) -> LMADDim (TPrimExp t num) -> TPrimExp Bool num+dynamicEqualsLMADDim dim1 dim2 =+ ldStride dim1 .==. ldStride dim2+ .&&. ldShape dim1 .==. ldShape dim2+ .&&. fromBool (ldPerm dim1 == ldPerm dim2)+ .&&. fromBool (ldMon dim1 == ldMon dim2)++-- | Dynamically determine if two 'LMAD' are equal.+--+-- True if offset and constituent 'LMADDim' are equal.+dynamicEqualsLMAD :: Eq num => LMAD (TPrimExp t num) -> LMAD (TPrimExp t num) -> TPrimExp Bool num+dynamicEqualsLMAD lmad1 lmad2 =+ lmadOffset lmad1 .==. lmadOffset lmad2+ .&&. foldr+ ((.&&.) . uncurry dynamicEqualsLMADDim)+ true+ (zip (lmadDims lmad1) (lmadDims lmad2))
src/Futhark/IR/Mem/Simplify.hs view
@@ -144,7 +144,7 @@ v_copy <- newVName $ baseString v <> "_nonext_copy" let v_pat = Pat [PatElem v_copy $ MemArray pt shape u $ ArrayIn mem ixfun]- addStm $ mkWiseStm v_pat (defAux ()) $ BasicOp (Copy v)+ addStm $ mkWiseStm v_pat (defAux ()) $ BasicOp $ Replicate mempty $ Var v pure $ SubExpRes cs $ Var v_copy | Just mem <- lookup (patElemName pat_elem) oldmem_to_mem = pure $ SubExpRes cs $ Var mem
src/Futhark/IR/Parse.hs view
@@ -1,20 +1,26 @@ -- | Parser for the Futhark core language. module Futhark.IR.Parse- ( parseSOACS,+ ( -- * Programs+ parseSOACS, parseGPU, parseGPUMem, parseMC, parseMCMem, parseSeq, parseSeqMem,++ -- * Fragments+ parseDeclExtType,+ parseDeclType, ) where import Data.Char (isAlpha) import Data.Functor-import Data.List (zipWith4)+import Data.List (singleton, zipWith4) import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE+import Data.Maybe import Data.Set qualified as S import Data.Text qualified as T import Data.Void@@ -54,13 +60,13 @@ pVName :: Parser VName pVName = lexeme $ do (s, tag) <-- satisfy constituent+ choice [exprBox, singleton <$> satisfy constituent] `manyTill_` try pTag <?> "variable name"- pure $ VName (nameFromString s) tag+ pure $ VName (nameFromString $ concat s) tag where- pTag =- "_" *> L.decimal <* notFollowedBy (satisfy constituent)+ pTag = "_" *> L.decimal <* notFollowedBy (satisfy constituent)+ exprBox = ("<{" <>) . (<> "}>") <$> (chunk "<{" *> manyTill anySingle (chunk "}>")) pBool :: Parser Bool pBool = choice [keyword "true" $> True, keyword "false" $> False]@@ -257,7 +263,7 @@ keyword "trace" $> uncurry (Opaque . OpaqueTrace) <*> parens ((,) <$> pStringLiteral <* pComma <*> pSubExp),- keyword "copy" $> Copy <*> parens pVName,+ keyword "copy" $> Replicate mempty . Var <*> parens pVName, keyword "assert" *> parens ( Assert@@ -267,9 +273,6 @@ <* pComma <*> pErrorLoc ),- keyword "rotate"- *> parens- (Rotate <$> parens (pSubExp `sepBy` pComma) <* pComma <*> pVName), keyword "replicate" *> parens (Replicate <$> pShape <* pComma <*> pSubExp), keyword "reshape"@@ -373,9 +376,15 @@ pExpDec :: ExpDec rep } -pRetTypes :: PR rep -> Parser [RetType rep]-pRetTypes pr = braces $ pRetType pr `sepBy` pComma+pRetAls :: Parser RetAls+pRetAls = fromMaybe (RetAls mempty mempty) <$> optional p+ where+ p = lexeme "#" *> parens (RetAls <$> pInts <* pComma <*> pInts)+ pInts = brackets $ pInt `sepBy` pComma +pRetTypes :: PR rep -> Parser [(RetType rep, RetAls)]+pRetTypes pr = braces $ ((,) <$> pRetType pr <*> pRetAls) `sepBy` pComma+ pBranchTypes :: PR rep -> Parser [BranchType rep] pBranchTypes pr = braces $ pBranchType pr `sepBy` pComma @@ -1107,11 +1116,14 @@ where op = pMemOp $ pMCOp prMCMem empty -parseRep :: PR rep -> FilePath -> T.Text -> Either T.Text (Prog rep)-parseRep pr fname s =+parseFull :: Parser a -> FilePath -> T.Text -> Either T.Text a+parseFull p fname s = either (Left . T.pack . errorBundlePretty) Right $- parse (whitespace *> pProg pr <* eof) fname s+ parse (whitespace *> p <* eof) fname s +parseRep :: PR rep -> FilePath -> T.Text -> Either T.Text (Prog rep)+parseRep = parseFull . pProg+ parseSOACS :: FilePath -> T.Text -> Either T.Text (Prog SOACS) parseSOACS = parseRep prSOACS @@ -1132,3 +1144,9 @@ parseMCMem :: FilePath -> T.Text -> Either T.Text (Prog MCMem) parseMCMem = parseRep prMCMem++parseDeclExtType :: FilePath -> T.Text -> Either T.Text DeclExtType+parseDeclExtType = parseFull pDeclExtType++parseDeclType :: FilePath -> T.Text -> Either T.Text DeclType+parseDeclType = parseFull pDeclType
src/Futhark/IR/Pretty.hs view
@@ -43,9 +43,6 @@ pretty Commutative = "commutative" pretty Noncommutative = "noncommutative" -instance Pretty NoUniqueness where- pretty _ = mempty- instance Pretty Shape where pretty = mconcat . map (brackets . pretty) . shapeDims @@ -220,6 +217,7 @@ pretty (Iota e x s et) = "iota" <> et' <> apply [pretty e, pretty x, pretty s] where et' = pretty $ show $ primBitSize $ IntType et+ pretty (Replicate (Shape []) e) = "copy" <> parens (pretty e) pretty (Replicate ne ve) = "replicate" <> apply [pretty ne, align (pretty ve)] pretty (Scratch t shape) =@@ -230,11 +228,8 @@ "coerce" <> apply [pretty shape, pretty e] pretty (Rearrange perm e) = "rearrange" <> apply [apply (map pretty perm), pretty e]- pretty (Rotate es e) =- "rotate" <> apply [apply (map pretty es), pretty e] pretty (Concat i (x :| xs) w) = "concat" <> "@" <> pretty i <> apply (pretty w : pretty x : map pretty xs)- pretty (Copy e) = "copy" <> parens (pretty e) pretty (Manifest perm e) = "manifest" <> apply [apply (map pretty perm), pretty e] pretty (Assert e msg (loc, _)) = "assert" <> apply [pretty e, pretty msg, pretty $ show $ locStr loc]@@ -261,6 +256,16 @@ pretty (Case vs b) = "case" <+> ppTuple' (map (maybe "_" pretty) vs) <+> "->" <+> maybeNest b +prettyRet :: Pretty t => (t, RetAls) -> Doc a+prettyRet (t, RetAls pals rals)+ | pals == mempty,+ rals == mempty =+ pretty t+ | otherwise =+ pretty t <> "#" <> parens (pl pals <> comma <+> pl rals)+ where+ pl = brackets . commasep . map pretty+ instance PrettyRep rep => Pretty (Exp rep) where pretty (Match [c] [Case [Just (BoolValue True)] t] f (MatchDec ret ifsort)) = "if"@@ -296,7 +301,7 @@ <+> pretty (nameToString fname) <> apply (map (align . prettyArg) args) </> colon- <+> braces (commasep $ map pretty ret)+ <+> braces (commasep $ map prettyRet ret) where prettyArg (arg, Consume) = "*" <> pretty arg prettyArg (arg, _) = pretty arg@@ -379,7 +384,7 @@ fun </> indent 2 (pretty (nameToString name)) <+> parens (commastack $ map pretty fparams)- </> indent 2 (colon <+> align (ppTupleLines' $ map pretty rettype))+ </> indent 2 (colon <+> align (ppTupleLines' $ map prettyRet rettype)) <+> equals <+> nestedBlock "{" "}" (pretty body) where
src/Futhark/IR/Prop.hs view
@@ -118,7 +118,6 @@ safeBasicOp Manifest {} = True safeBasicOp Iota {} = True safeBasicOp Replicate {} = True- safeBasicOp Copy {} = True safeBasicOp _ = False safeExp (DoLoop _ _ body) = safeBody body safeExp (Apply fname _ _ _) =
src/Futhark/IR/Prop/Aliases.hs view
@@ -71,9 +71,7 @@ basicOpAliases Scratch {} = [mempty] basicOpAliases (Reshape _ _ e) = [vnameAliases e] basicOpAliases (Rearrange _ e) = [vnameAliases e]-basicOpAliases (Rotate _ e) = [vnameAliases e] basicOpAliases Concat {} = [mempty]-basicOpAliases Copy {} = [mempty] basicOpAliases Manifest {} = [mempty] basicOpAliases Assert {} = [mempty] basicOpAliases UpdateAcc {} = [mempty]@@ -84,23 +82,30 @@ where (alses, conses) = unzip l -returnAliases :: [TypeBase shape Uniqueness] -> [(Names, Diet)] -> [Names]-returnAliases rts args = map returnType' rts+funcallAliases ::+ [PatElem dec] ->+ [(SubExp, Diet)] ->+ [(TypeBase shape Uniqueness, RetAls)] ->+ [Names]+funcallAliases pes args = map onType where- returnType' (Array _ _ Nonunique) =- mconcat $ map (uncurry maskAliases) args- returnType' (Array _ _ Unique) =- mempty- returnType' (Prim _) =- mempty- returnType' Acc {} =- error "returnAliases Acc"- returnType' Mem {} =- mconcat $ map (uncurry maskAliases) args+ getAls als is = mconcat $ map fst $ filter ((`elem` is) . snd) $ zip als [0 ..]+ arg_als = map (subExpAliases . fst) args+ res_als = map (oneName . patElemName) pes+ onType (_t, RetAls pals rals) = getAls arg_als pals <> getAls res_als rals -funcallAliases :: [(SubExp, Diet)] -> [TypeBase shape Uniqueness] -> [Names]-funcallAliases args t =- returnAliases t [(subExpAliases se, d) | (se, d) <- args]+mutualAliases :: Names -> [PatElem dec] -> [Names] -> [Names]+mutualAliases bound pes als = zipWith grow (map patElemName pes) als+ where+ bound_als = map (`namesIntersection` bound) als+ grow v names = (names <> pe_names) `namesSubtract` bound+ where+ pe_names =+ namesFromList+ . filter (/= v)+ . map (patElemName . fst)+ . filter (namesIntersect names . snd)+ $ zip pes bound_als -- | The aliases of an expression, one for each pattern element. --@@ -111,27 +116,19 @@ expAliases pes (Match _ cases defbody _) = -- Repeat mempty in case the pattern has more elements (this -- implies a type error).- zipWith grow (map patElemName pes) $ als ++ repeat mempty+ mutualAliases bound pes $ als ++ repeat mempty where als = matchAliases $ onBody defbody : map (onBody . caseBody) cases onBody body = (bodyAliases body, consumedInBody body) bound = foldMap boundInBody $ defbody : map caseBody cases- grow v names = (names <> pe_names) `namesSubtract` bound- where- pe_names =- namesFromList- . filter (/= v)- . map (patElemName . fst)- . filter (namesIntersect names . snd)- $ zip pes als expAliases _ (BasicOp op) = basicOpAliases op-expAliases _ (DoLoop merge _ loopbody) = do- (p, als) <-- transitive . zip params $ zipWith mappend arg_aliases (bodyAliases loopbody)- let als' = als `namesSubtract` param_names- if unique $ paramDeclType p- then pure mempty- else pure $ als' `namesSubtract` bound+expAliases pes (DoLoop merge _ loopbody) =+ mutualAliases (bound <> param_names) pes $ do+ (p, als) <-+ transitive . zip params $ zipWith (<>) arg_aliases (bodyAliases loopbody)+ if unique $ paramDeclType p+ then pure mempty+ else pure als where bound = boundInBody loopbody arg_aliases = map (subExpAliases . snd) merge@@ -145,8 +142,8 @@ where look v = maybe mempty snd $ find ((== v) . paramName . fst) merge_and_als expand als = als <> foldMap look (namesToList als)-expAliases _ (Apply _ args t _) =- funcallAliases args $ map declExtTypeOf t+expAliases pes (Apply _ args t _) =+ funcallAliases pes args $ map (first declExtTypeOf) t expAliases _ (WithAcc inputs lam) = concatMap inputAliases inputs ++ drop num_accs (map (`namesSubtract` boundInBody body) $ bodyAliases body)@@ -155,11 +152,6 @@ inputAliases (_, arrs, _) = replicate (length arrs) mempty num_accs = length inputs expAliases _ (Op op) = opAliases op--maskAliases :: Names -> Diet -> Names-maskAliases _ Consume = mempty-maskAliases _ ObservePrim = mempty-maskAliases als Observe = als -- | The variables consumed in this statement. consumedInStm :: Aliased rep => Stm rep -> Names
src/Futhark/IR/Prop/Names.hs view
@@ -239,7 +239,7 @@ where freeIn' (FunDef _ _ _ rettype params body) = fvBind (namesFromList $ map paramName params) $- freeIn' rettype <> freeIn' params <> freeIn' body+ foldMap (freeIn' . fst) rettype <> freeIn' params <> freeIn' body instance ( FreeDec (ExpDec rep),
src/Futhark/IR/Prop/TypeOf.hs view
@@ -109,14 +109,10 @@ result <$> lookupType e where result t = [rearrangeType perm t]-basicOpType (Rotate _ e) =- pure <$> lookupType e basicOpType (Concat i (x :| _) ressize) = result <$> lookupType x where result xt = [setDimSize i xt ressize]-basicOpType (Copy v) =- pure <$> lookupType v basicOpType (Manifest _ v) = pure <$> lookupType v basicOpType Assert {} =@@ -129,7 +125,7 @@ (HasScope rep m, TypedOp (Op rep)) => Exp rep -> m [ExtType]-expExtType (Apply _ _ rt _) = pure $ map (fromDecl . declExtTypeOf) rt+expExtType (Apply _ _ rt _) = pure $ map (fromDecl . declExtTypeOf . fst) rt expExtType (Match _ _ _ rt) = pure $ map extTypeOf $ matchReturns rt expExtType (DoLoop merge _ _) = pure $ loopExtType $ map fst merge
src/Futhark/IR/Prop/Types.hs view
@@ -589,6 +589,9 @@ instance ExtTyped ExtType where extTypeOf = id +instance ExtTyped DeclExtType where+ extTypeOf = fromDecl . declExtTypeOf+ -- | Typeclass for things that contain 'DeclExtType's. class FixExt t => DeclExtTyped t where declExtTypeOf :: t -> DeclExtType
src/Futhark/IR/Rephrase.hs view
@@ -15,6 +15,7 @@ ) where +import Data.Bitraversable import Futhark.IR.Syntax import Futhark.IR.Traversals @@ -46,7 +47,7 @@ rephraseFunDef rephraser fundec = do body' <- rephraseBody rephraser $ funDefBody fundec params' <- mapM (rephraseParam $ rephraseFParamDec rephraser) $ funDefParams fundec- rettype' <- mapM (rephraseRetType rephraser) $ funDefRetType fundec+ rettype' <- mapM (bitraverse (rephraseRetType rephraser) pure) $ funDefRetType fundec pure fundec {funDefBody = body', funDefParams = params', funDefRetType = rettype'} -- | Rephrase an expression.
src/Futhark/IR/RetType.hs view
@@ -28,7 +28,7 @@ -- a list of these will be used. It should contain at least the -- information contained in an 'ExtType', but may have more, notably -- an existential context.-class (Show rt, Eq rt, Ord rt, DeclExtTyped rt) => IsRetType rt where+class (Show rt, Eq rt, Ord rt, ExtTyped rt, DeclExtTyped rt) => IsRetType rt where -- | Contruct a return type from a primitive type. primRetType :: PrimType -> rt
src/Futhark/IR/SOACS/Simplify.hs view
@@ -271,7 +271,7 @@ where e inp = case patElemType outId of Acc {} -> BasicOp $ SubExp $ Var inp- _ -> BasicOp (Copy inp)+ _ -> BasicOp (Replicate mempty (Var inp)) checkInvariance (outId, SubExpRes _ e, t) (invariant, mapresult, rettype') | freeOrConst e = ( (Pat [outId], BasicOp $ Replicate (Shape [w]) e) : invariant,@@ -308,7 +308,7 @@ partitionEithers $ map isInvariantRes $ zip3 map_ts map_pes map_res, not $ null invariant_map = Simplify $ do forM_ invariant_map $ \(pe, arr) ->- letBind (Pat [pe]) $ BasicOp $ Copy arr+ letBind (Pat [pe]) $ BasicOp $ Replicate mempty $ Var arr let (variant_map_ts, variant_map_pes, variant_map_res) = unzip3 variant_map lam' =@@ -462,7 +462,7 @@ } auxing aux $ letBind (Pat pes') $ Op $ Screma w arrs $ mapSOAC fun' forM_ copies $ \(from, to) ->- letBind (Pat [to]) $ BasicOp $ Copy $ patElemName from+ letBind (Pat [to]) $ BasicOp $ Replicate mempty $ Var $ patElemName from where checkForDuplicates (ses_ts_pes', copies) (se, t, pe) | Just (_, _, pe') <- find (\(x, _, _) -> resSubExp x == resSubExp se) ses_ts_pes' =@@ -493,12 +493,6 @@ not $ UT.isConsumed (patElemName map_pe) used = Simplify . certifying (stmAuxCerts aux1 <> cs) . letBind pat . BasicOp $ Rearrange (0 : map (1 +) perm) arr- | Just (map_pe, cs, _, BasicOp (Rotate rots rotate_arr), [p], [arr]) <-- isMapWithOp pat e,- paramName p == rotate_arr,- not $ UT.isConsumed (patElemName map_pe) used =- Simplify . certifying (stmAuxCerts aux1 <> cs) . letBind pat . BasicOp $- Rotate (intConst Int64 0 : rots) arr mapOpToOp _ _ _ _ = Skip isMapWithOp ::@@ -696,7 +690,6 @@ data ArrayOp = ArrayIndexing Certs VName (Slice SubExp) | ArrayRearrange Certs VName [Int]- | ArrayRotate Certs VName [SubExp] | ArrayReshape Certs VName ReshapeKind Shape | ArrayCopy Certs VName | -- | Never constructed.@@ -706,7 +699,6 @@ arrayOpArr :: ArrayOp -> VName arrayOpArr (ArrayIndexing _ arr _) = arr arrayOpArr (ArrayRearrange _ arr _) = arr-arrayOpArr (ArrayRotate _ arr _) = arr arrayOpArr (ArrayReshape _ arr _ _) = arr arrayOpArr (ArrayCopy _ arr) = arr arrayOpArr (ArrayVar _ arr) = arr@@ -714,7 +706,6 @@ arrayOpCerts :: ArrayOp -> Certs arrayOpCerts (ArrayIndexing cs _ _) = cs arrayOpCerts (ArrayRearrange cs _ _) = cs-arrayOpCerts (ArrayRotate cs _ _) = cs arrayOpCerts (ArrayReshape cs _ _ _) = cs arrayOpCerts (ArrayCopy cs _) = cs arrayOpCerts (ArrayVar cs _) = cs@@ -724,11 +715,9 @@ Just $ ArrayIndexing cs arr slice isArrayOp cs (BasicOp (Rearrange perm arr)) = Just $ ArrayRearrange cs arr perm-isArrayOp cs (BasicOp (Rotate rots arr)) =- Just $ ArrayRotate cs arr rots isArrayOp cs (BasicOp (Reshape k new_shape arr)) = Just $ ArrayReshape cs arr k new_shape-isArrayOp cs (BasicOp (Copy arr)) =+isArrayOp cs (BasicOp (Replicate (Shape []) (Var arr))) = Just $ ArrayCopy cs arr isArrayOp _ _ = Nothing@@ -736,9 +725,8 @@ fromArrayOp :: ArrayOp -> (Certs, Exp rep) fromArrayOp (ArrayIndexing cs arr slice) = (cs, BasicOp $ Index arr slice) fromArrayOp (ArrayRearrange cs arr perm) = (cs, BasicOp $ Rearrange perm arr)-fromArrayOp (ArrayRotate cs arr rots) = (cs, BasicOp $ Rotate rots arr) fromArrayOp (ArrayReshape cs arr k new_shape) = (cs, BasicOp $ Reshape k new_shape arr)-fromArrayOp (ArrayCopy cs arr) = (cs, BasicOp $ Copy arr)+fromArrayOp (ArrayCopy cs arr) = (cs, BasicOp $ Replicate mempty $ Var arr) fromArrayOp (ArrayVar cs arr) = (cs, BasicOp $ SubExp $ Var arr) arrayOps ::@@ -935,9 +923,6 @@ arr `elem` map_param_names && all (`ST.elem` vtable) (namesToList $ freeIn cs) && not (null perm)- arrayIsMapParam (_, ArrayRotate cs arr rots) =- arr `elem` map_param_names- && all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn rots) arrayIsMapParam (_, ArrayReshape cs arr _ new_shape) = arr `elem` map_param_names && all (`ST.elem` vtable) (namesToList $ freeIn cs <> freeIn new_shape)@@ -959,12 +944,10 @@ BasicOp $ Index arr $ Slice $ whole_dim : slice ArrayRearrange _ _ perm -> BasicOp $ Rearrange (0 : map (+ 1) perm) arr- ArrayRotate _ _ rots ->- BasicOp $ Rotate (intConst Int64 0 : rots) arr ArrayReshape _ _ k new_shape -> BasicOp $ Reshape k (Shape [w] <> new_shape) arr ArrayCopy {} ->- BasicOp $ Copy arr+ BasicOp $ Replicate mempty $ Var arr ArrayVar {} -> BasicOp $ SubExp $ Var arr arr_transformed_t <- lookupType arr_transformed
src/Futhark/IR/SegOp.hs view
@@ -381,7 +381,7 @@ pretty (KernelBody _ stms res) = PP.stack (map pretty (stmsToList stms)) </> "return"- <+> PP.braces (PP.commasep $ map pretty res)+ <+> PP.braces (PP.commastack $ map pretty res) certAnnots :: Certs -> [Doc ann] certAnnots cs@@ -1393,7 +1393,7 @@ then do precopy <- newVName $ baseString (patElemName kpe) <> "_precopy" index kpe {patElemName = precopy}- letBindNames [patElemName kpe] $ BasicOp $ Copy precopy+ letBindNames [patElemName kpe] $ BasicOp $ Replicate mempty $ Var precopy else index kpe pure ( kpes'',
src/Futhark/IR/Syntax.hs view
@@ -139,6 +139,7 @@ MatchSort (..), Safety (..), Lambda (..),+ RetAls (..), -- * Definitions Param (..),@@ -355,8 +356,6 @@ -- -- @concat(1, [[1,2], [3, 4]] :| [[[5,6]], [[7, 8]]], 4) = [[1, 2, 5, 6], [3, 4, 7, 8]]@ Concat Int (NonEmpty VName) SubExp- | -- | Copy the given array. The result will not alias anything.- Copy VName | -- | Manifest an array with dimensions represented in the given -- order. The result will not alias anything. Manifest [Int] VName@@ -367,7 +366,8 @@ -- The t'IntType' indicates the type of the array returned and the -- offset/stride arguments, but not the length argument. Iota SubExp SubExp SubExp IntType- | -- | @replicate([3][2],1) = [[1,1], [1,1], [1,1]]@+ | -- | @replicate([3][2],1) = [[1,1], [1,1], [1,1]]@. The result+ -- has no aliases. Copy a value by passing an empty shape. Replicate Shape SubExp | -- | Create array of given type and shape, with undefined elements. Scratch PrimType [SubExp]@@ -378,10 +378,6 @@ -- must be a permutation of @[0,n-1]@, where @n@ is the -- number of dimensions in the input array. Rearrange [Int] VName- | -- | Rotate the dimensions of the input array. The list of- -- subexpressions specify how much each dimension is rotated. The- -- length of this list must be equal to the rank of the array.- Rotate [SubExp] VName | -- | Update an accumulator at the given index with the given value. -- Consumes the accumulator and produces a new one. UpdateAcc VName [SubExp] [SubExp]@@ -409,13 +405,30 @@ instance Traversable Case where traverse f (Case vs b) = Case vs <$> f b +-- | Information about the possible aliases of a function result.+data RetAls = RetAls+ { -- | Which of the parameters may be aliased, numbered from zero.+ paramAls :: [Int],+ -- | Which of the other results may be aliased, numbered from+ -- zero. This must be a reflexive relation.+ otherAls :: [Int]+ }+ deriving (Eq, Ord, Show)++instance Monoid RetAls where+ mempty = RetAls mempty mempty++instance Semigroup RetAls where+ RetAls pals1 rals1 <> RetAls pals2 rals2 =+ RetAls (pals1 <> pals2) (rals1 <> rals2)+ -- | The root Futhark expression type. The v'Op' constructor contains -- a rep-specific operation. Do-loops, branches and function calls -- are special. Everything else is a simple t'BasicOp'. data Exp rep = -- | A simple (non-recursive) operation. BasicOp BasicOp- | Apply Name [(SubExp, Diet)] [RetType rep] (Safety, SrcLoc, [SrcLoc])+ | Apply Name [(SubExp, Diet)] [(RetType rep, RetAls)] (Safety, SrcLoc, [SrcLoc]) | -- | A match statement picks a branch by comparing the given -- subexpressions (called the /scrutinee/) with the pattern in -- each of the cases. If none of the cases match, the /default@@ -509,7 +522,7 @@ funDefEntryPoint :: Maybe EntryPoint, funDefAttrs :: Attrs, funDefName :: Name,- funDefRetType :: [RetType rep],+ funDefRetType :: [(RetType rep, RetAls)], funDefParams :: [FParam rep], funDefBody :: Body rep }
src/Futhark/IR/Syntax/Core.hs view
@@ -12,7 +12,6 @@ -- * Types Commutativity (..), Uniqueness (..),- NoUniqueness (..), ShapeBase (..), Shape, stripDims,@@ -221,16 +220,6 @@ -- | A string representing a specific non-default memory space. type SpaceId = String --- | A fancier name for @()@ - encodes no uniqueness information.-data NoUniqueness = NoUniqueness- deriving (Eq, Ord, Show)--instance Semigroup NoUniqueness where- NoUniqueness <> NoUniqueness = NoUniqueness--instance Monoid NoUniqueness where- mempty = NoUniqueness- -- | The type of a value. When comparing types for equality with -- '==', shapes must match. data TypeBase shape u@@ -248,7 +237,13 @@ bitraverse _ _ (Mem s) = pure $ Mem s instance Functor (TypeBase shape) where- fmap = second+ fmap = fmapDefault++instance Foldable (TypeBase shape) where+ foldMap = foldMapDefault++instance Traversable (TypeBase shape) where+ traverse = bitraverse pure instance Bifunctor TypeBase where bimap = bimapDefault
src/Futhark/IR/Traversals.hs view
@@ -114,7 +114,7 @@ mapExpM tv (Apply fname args ret loc) = do args' <- forM args $ \(arg, d) -> (,) <$> mapOnSubExp tv arg <*> pure d- Apply fname args' <$> mapM (mapOnRetType tv) ret <*> pure loc+ Apply fname args' <$> mapM (bitraverse (mapOnRetType tv) pure) ret <*> pure loc mapExpM tv (BasicOp (Index arr slice)) = BasicOp <$> ( Index@@ -155,15 +155,11 @@ ) mapExpM tv (BasicOp (Rearrange perm e)) = BasicOp <$> (Rearrange perm <$> mapOnVName tv e)-mapExpM tv (BasicOp (Rotate es e)) =- BasicOp <$> (Rotate <$> mapM (mapOnSubExp tv) es <*> mapOnVName tv e) mapExpM tv (BasicOp (Concat i (x :| ys) size)) = do x' <- mapOnVName tv x ys' <- mapM (mapOnVName tv) ys size' <- mapOnSubExp tv size pure $ BasicOp $ Concat i (x' :| ys') size'-mapExpM tv (BasicOp (Copy e)) =- BasicOp <$> (Copy <$> mapOnVName tv e) mapExpM tv (BasicOp (Manifest perm e)) = BasicOp <$> (Manifest perm <$> mapOnVName tv e) mapExpM tv (BasicOp (Assert e msg loc)) =@@ -308,8 +304,9 @@ mapM_ (walkOnBody tv mempty . caseBody) cases walkOnBody tv mempty defbody mapM_ (walkOnBranchType tv) ts-walkExpM tv (Apply _ args ret _) =- mapM_ (walkOnSubExp tv . fst) args >> mapM_ (walkOnRetType tv) ret+walkExpM tv (Apply _ args ret _) = do+ mapM_ (walkOnSubExp tv . fst) args+ mapM_ (walkOnRetType tv . fst) ret walkExpM tv (BasicOp (Index arr slice)) = walkOnVName tv arr >> traverse_ (walkOnSubExp tv) slice walkExpM tv (BasicOp (Update _ arr slice se)) =@@ -332,12 +329,8 @@ mapM_ (walkOnSubExp tv) shape >> walkOnVName tv arrexp walkExpM tv (BasicOp (Rearrange _ e)) = walkOnVName tv e-walkExpM tv (BasicOp (Rotate es e)) =- mapM_ (walkOnSubExp tv) es >> walkOnVName tv e walkExpM tv (BasicOp (Concat _ (x :| ys) size)) = walkOnVName tv x >> mapM_ (walkOnVName tv) ys >> walkOnSubExp tv size-walkExpM tv (BasicOp (Copy e)) =- walkOnVName tv e walkExpM tv (BasicOp (Manifest _ e)) = walkOnVName tv e walkExpM tv (BasicOp (Assert e msg _)) =
src/Futhark/IR/TypeCheck.hs view
@@ -46,11 +46,11 @@ import Control.Monad.Reader import Control.Monad.State.Strict import Control.Parallel.Strategies+import Data.Bifunctor (first) import Data.List (find, intercalate, isPrefixOf, sort) import Data.List.NonEmpty (NonEmpty (..)) import Data.Map.Strict qualified as M import Data.Maybe-import Data.Set qualified as S import Data.Text qualified as T import Futhark.Analysis.Alias import Futhark.Analysis.PrimExp@@ -189,7 +189,7 @@ -- | A tuple of a return type and a list of parameters, possibly -- named.-type FunBinding rep = ([RetType (Aliases rep)], [FParam (Aliases rep)])+type FunBinding rep = ([(RetType (Aliases rep), RetAls)], [FParam (Aliases rep)]) type VarBinding rep = NameInfo (Aliases rep) @@ -439,18 +439,18 @@ TypeM rep a binding stms = check . local (`bindVars` stms) where- bindVars = M.foldlWithKey' bindVar+ bindVars orig_env = M.foldlWithKey' (bindVar orig_env) orig_env boundnames = M.keys stms - bindVar env name (LetName (AliasDec als, dec)) =+ bindVar orig_env env name (LetName (AliasDec als, dec)) = let als' | primType (typeOf dec) = mempty- | otherwise = expandAliases als env+ | otherwise = expandAliases als orig_env in env { envVtable = M.insert name (LetName (AliasDec als', dec)) $ envVtable env }- bindVar env name dec =+ bindVar _ env name dec = env {envVtable = M.insert name dec $ envVtable env} -- Check whether the bound variables have been used correctly@@ -488,18 +488,18 @@ Checkable rep => Name -> [SubExp] ->- TypeM rep ([RetType rep], [DeclType])+ TypeM rep ([(RetType rep, RetAls)], [DeclType]) lookupFun fname args = do stm <- asks $ M.lookup fname . envFtable case stm of Nothing -> bad $ UnknownFunctionError fname Just (ftype, params) -> do argts <- mapM subExpType args- case applyRetType ftype params $ zip args argts of+ case applyRetType (map fst ftype) params $ zip args argts of Nothing -> bad $ ParameterMismatch (Just fname) (map paramType params) argts Just rt ->- pure (rt, map paramDeclType params)+ pure (zip rt $ map snd ftype, map paramDeclType params) -- | @checkAnnotation loc s t1 t2@ checks if @t2@ is equal to -- @t1@. If not, a 'BadAnnotation' is raised.@@ -610,7 +610,7 @@ where addBuiltin (fname, (t, ts)) = do ps <- mapM (primFParam name) ts- pure (fname, ([primRetType t], ps))+ pure (fname, ([(primRetType t, RetAls mempty mempty)], ps)) name = VName (nameFromString "x") 0 checkFun ::@@ -621,13 +621,13 @@ context ("In function " <> nameToText fname) $ checkFun' ( fname,- map declExtTypeOf rettype,+ map (first declExtTypeOf) rettype, funParamsToNameInfos params ) (Just consumable) $ do checkFunParams params- checkRetType rettype+ checkRetType $ map fst rettype context "When checking function body" $ checkFunBody rettype body where consumable =@@ -662,59 +662,72 @@ context ("In lambda parameter " <> prettyText param) $ checkLParamDec (paramName param) (paramDec param) +checkNoDuplicateParams :: Name -> [VName] -> TypeM rep ()+checkNoDuplicateParams fname = foldM_ expand []+ where+ expand seen pname+ | Just _ <- find (== pname) seen =+ bad $ DupParamError fname pname+ | otherwise =+ pure $ pname : seen+ checkFun' :: Checkable rep => ( Name,- [DeclExtType],+ [(DeclExtType, RetAls)], [(VName, NameInfo (Aliases rep))] ) -> Maybe [(VName, Names)] -> TypeM rep [Names] -> TypeM rep () checkFun' (fname, rettype, params) consumable check = do- checkNoDuplicateParams+ checkNoDuplicateParams fname param_names binding (M.fromList params) $ maybe id consumeOnlyParams consumable $ do body_aliases <- check- scope <- askScope- let isArray = maybe False ((> 0) . arrayRank . typeOf) . (`M.lookup` scope) context ( "When checking the body aliases: " <> prettyText (map namesToList body_aliases) )- $ checkReturnAlias- $ map (namesFromList . filter isArray . namesToList) body_aliases+ $ checkReturnAlias body_aliases where param_names = map fst params - checkNoDuplicateParams = foldM_ expand [] param_names+ isParam = (`elem` param_names) - expand seen pname- | Just _ <- find (== pname) seen =- bad $ DupParamError fname pname- | otherwise =- pure $ pname : seen- checkReturnAlias =- foldM_ checkReturnAlias' mempty . returnAliasing rettype+ unique_names = namesFromList $ do+ (v, FParamName t) <- params+ guard $ unique $ declTypeOf t+ pure v - checkReturnAlias' seen (Unique, names)- | any (`S.member` S.map fst seen) $ namesToList names =- bad $ UniqueReturnAliased fname- | otherwise = do- consume names- pure $ seen <> tag Unique names- checkReturnAlias' seen (Nonunique, names)- | any (`S.member` seen) $ tag Unique names =- bad $ UniqueReturnAliased fname- | otherwise = pure $ seen <> tag Nonunique names+ allowedArgAliases pals =+ namesFromList (map (param_names !!) pals) <> unique_names - tag u = S.fromList . map (,u) . namesToList+ checkReturnAlias retals = zipWithM_ checkRet (zip [(0 :: Int) ..] rettype) retals+ where+ comrades = zip3 [0 ..] retals $ map (otherAls . snd) rettype - returnAliasing expected got =- reverse $- zip (reverse (map uniqueness expected) ++ repeat Nonunique) $- reverse got+ checkRet (i, (Array {}, RetAls pals rals)) als+ | als'' <- filter isParam $ namesToList als',+ not $ null als'' =+ bad . TypeError . T.unlines $+ [ T.unwords ["Result", prettyText i, "aliases", prettyText als''],+ T.unwords ["but is only allowed to alias arguments", prettyText allowed_args]+ ]+ | ((j, _, _) : _) <- filter (isProblem i als' rals) comrades =+ bad . TypeError . T.unlines $+ [ T.unwords ["Results", prettyText i, "and", prettyText j, "alias each other"],+ T.unwords ["but result", prettyText i, "only allowed to alias results", prettyText rals],+ prettyText retals+ ]+ where+ allowed_args = allowedArgAliases pals+ als' = als `namesSubtract` allowed_args+ checkRet _ _ = pure () + isProblem i als rals (j, jals, j_rals) =+ i /= j && j `notElem` rals && i `notElem` j_rals && namesIntersect als jals+ checkSubExp :: Checkable rep => SubExp -> TypeM rep Type checkSubExp (Constant val) = pure $ Prim $ primValueType val@@ -753,7 +766,7 @@ checkFunBody :: Checkable rep =>- [RetType rep] ->+ [(RetType rep, RetAls)] -> Body (Aliases rep) -> TypeM rep [Names] checkFunBody rt (Body (_, rep) stms res) = do@@ -761,23 +774,17 @@ checkStms stms $ do context "When checking body result" $ checkResult res context "When matching declared return type to result of body" $- matchReturnType rt res- map (`namesSubtract` bound_here) <$> mapM (subExpAliasesM . resSubExp) res- where- bound_here = namesFromList $ M.keys $ scopeOf stms+ matchReturnType (map fst rt) res+ mapM (subExpAliasesM . resSubExp) res checkLambdaBody :: Checkable rep => [Type] -> Body (Aliases rep) ->- TypeM rep [Names]+ TypeM rep () checkLambdaBody ret (Body (_, rep) stms res) = do checkBodyDec rep- checkStms stms $ do- checkLambdaResult ret res- map (`namesSubtract` bound_here) <$> mapM (subExpAliasesM . resSubExp) res- where- bound_here = namesFromList $ M.keys $ scopeOf stms+ checkStms stms $ checkLambdaResult ret res checkLambdaResult :: Checkable rep =>@@ -909,18 +916,6 @@ bad $ PermutationError perm rank $ Just arr-checkBasicOp (Rotate rots arr) = do- arrt <- lookupType arr- let rank = arrayRank arrt- mapM_ (require [Prim int64]) rots- when (length rots /= rank) $- bad $- TypeError $- "Cannot rotate "- <> prettyText (length rots)- <> " dimensions of "- <> prettyText rank- <> "-dimensional array." checkBasicOp (Concat i (arr1exp :| arr2exps) ressize) = do arr1_dims <- shapeDims . fst <$> checkArrIdent arr1exp arr2s_dims <- map (shapeDims . fst) <$> mapM checkArrIdent arr2exps@@ -928,8 +923,6 @@ bad $ TypeError "Types of arguments to concat do not match." require [Prim int64] ressize-checkBasicOp (Copy e) =- void $ checkArrIdent e checkBasicOp (Manifest perm arr) = checkBasicOp $ Rearrange perm arr -- Basically same thing! checkBasicOp (Assert e (ErrorMsg parts) _) = do@@ -986,6 +979,10 @@ (staticShapes rettype') (staticShapes bodyt) +allowAllAliases :: Int -> Int -> RetAls+allowAllAliases n m =+ RetAls [0 .. n - 1] [0 .. m - 1]+ checkExp :: Checkable rep => Exp (Aliases rep) ->@@ -1016,9 +1013,9 @@ when (rettype_derived /= rettype_annot) $ bad . TypeError . docText $ "Expected apply result type:"- </> indent 2 (pretty rettype_derived)+ </> indent 2 (pretty $ map fst rettype_derived) </> "But annotation is:"- </> indent 2 (pretty rettype_annot)+ </> indent 2 (pretty $ map fst rettype_annot) consumeArgs paramtypes argflows checkExp (DoLoop merge form loopbody) = do let (mergepat, mergeexps) = unzip merge@@ -1040,7 +1037,7 @@ context "Inside the loop body" $ checkFun' ( nameFromString "<loop body>",- staticShapes rettype,+ map (,allowAllAliases (length merge) (length merge)) (staticShapes rettype), funParamsToNameInfos mergepat ) (Just consumable)@@ -1281,23 +1278,9 @@ context "When checking expression annotation" $ checkExpDec dec context ("When matching\n" <> message " " pat <> "\nwith\n" <> message " " e) $ matchPat pat e- binding (maybeWithoutAliases $ scopeOf stm) $ do+ binding (scopeOf stm) $ do mapM_ checkPatElem (patElems $ removePatAliases pat) m- where- -- FIXME: this is wrong. However, the core language type system- -- is not strong enough to fully capture the aliases we want (see- -- issue #803). Since we eventually inline everything anyway, and- -- our intra-procedural alias analysis is much simpler and- -- correct, I could not justify spending time on improving the- -- inter-procedural alias analysis. If we ever stop inlining- -- everything, probably we need to go back and refine this.- maybeWithoutAliases =- case stmExp stm of- Apply {} -> M.map withoutAliases- _ -> id- withoutAliases (LetName (_, ldec)) = LetName (mempty, ldec)- withoutAliases info = info matchExtPat :: Checkable rep =>@@ -1429,10 +1412,9 @@ else Nothing params' = [(paramName param, LParamName $ paramDec param) | param <- params]- checkFun'- (fname, staticShapes $ map (`toDecl` Nonunique) rettype, params')- consumable- $ do+ checkNoDuplicateParams fname $ map paramName params+ binding (M.fromList params') $+ maybe id consumeOnlyParams consumable $ do checkLambdaParams params mapM_ checkType rettype checkLambdaBody rettype body
src/Futhark/Internalise.hs view
@@ -48,9 +48,11 @@ import Futhark.Internalise.Defunctorise as Defunctorise import Futhark.Internalise.Entry (visibleTypes) import Futhark.Internalise.Exps qualified as Exps+import Futhark.Internalise.FullNormalise qualified as FullNormalise import Futhark.Internalise.LiftLambdas as LiftLambdas import Futhark.Internalise.Monad as I import Futhark.Internalise.Monomorphise as Monomorphise+import Futhark.Internalise.ReplaceRecords as ReplaceRecords import Futhark.Util.Log import Language.Futhark.Semantic (Imports) @@ -64,14 +66,18 @@ internaliseProg config prog = do maybeLog "Defunctorising" prog_decs <- Defunctorise.transformProg prog+ maybeLog "Full Normalising"+ prog_decs' <- FullNormalise.transformProg prog_decs maybeLog "Monomorphising"- prog_decs' <- Monomorphise.transformProg prog_decs+ prog_decs'' <- Monomorphise.transformProg prog_decs'+ maybeLog "Replacing records"+ prog_decs''' <- ReplaceRecords.transformProg prog_decs'' maybeLog "Lifting lambdas"- prog_decs'' <- LiftLambdas.transformProg prog_decs'+ prog_decs'''' <- LiftLambdas.transformProg prog_decs''' maybeLog "Defunctionalising"- prog_decs''' <- Defunctionalise.transformProg prog_decs''+ prog_decs''''' <- Defunctionalise.transformProg prog_decs'''' maybeLog "Converting to core IR"- Exps.transformProg (futharkSafe config) (visibleTypes prog) prog_decs'''+ Exps.transformProg (futharkSafe config) (visibleTypes prog) prog_decs''''' where verbose = fst (futharkVerbose config) > NotVerbose maybeLog s
src/Futhark/Internalise/Bindings.hs view
@@ -12,8 +12,10 @@ where import Control.Monad+import Control.Monad.Free (Free (..)) import Control.Monad.Reader import Data.Bifunctor+import Data.Foldable (toList) import Data.Map.Strict qualified as M import Data.Maybe import Futhark.IR.SOACS qualified as I@@ -33,44 +35,64 @@ internaliseAttrs :: [E.AttrInfo VName] -> InternaliseM I.Attrs internaliseAttrs = fmap (mconcat . map I.oneAttr) . mapM internaliseAttr +treeLike :: Tree a -> [b] -> Tree b+treeLike (Pure _) [b] = Pure b+treeLike (Pure _) _ = error "treeLike: invalid input"+treeLike (Free ls) bs = Free $ zipWith treeLike ls (chunks (map length ls) bs)+ bindingFParams :: [E.TypeParam] ->- [E.Pat] ->- ([I.FParam I.SOACS] -> [[I.FParam I.SOACS]] -> InternaliseM a) ->+ [E.Pat E.ParamType] ->+ ([I.FParam I.SOACS] -> [[Tree (I.FParam I.SOACS)]] -> InternaliseM a) -> InternaliseM a bindingFParams tparams params m = do flattened_params <- mapM flattenPat params let params_idents = concat flattened_params params_ts <- internaliseParamTypes $- map (flip E.setAliases () . E.unInfo . E.identType . fst) params_idents+ map (E.unInfo . E.identType . fst) params_idents let num_param_idents = map length flattened_params- num_param_ts = map (sum . map length) $ chunks num_param_idents params_ts let shape_params = [I.Param mempty v $ I.Prim I.int64 | E.TypeParamDim v _ <- tparams] shape_subst = M.fromList [(I.paramName p, [I.Var $ I.paramName p]) | p <- shape_params]- bindingFlatPat params_idents (concat params_ts) $ \valueparams -> do- let (certparams, valueparams') = unzip $ map fixAccParam (concat valueparams)- I.localScope (I.scopeOfFParams $ catMaybes certparams ++ shape_params ++ valueparams') $- substitutingVars shape_subst $- m (catMaybes certparams ++ shape_params) $- chunks num_param_ts valueparams'+ bindingFlatPat params_idents (concatMap (concatMap toList) params_ts) $ \valueparams -> do+ let (certparams, valueparams') =+ first concat $ unzip $ map fixAccParams valueparams+ all_params = certparams ++ shape_params ++ concat valueparams'+ I.localScope (I.scopeOfFParams all_params) $+ substitutingVars shape_subst $ do+ let values_grouped_by_params = chunks num_param_idents valueparams'+ types_grouped_by_params = chunks num_param_idents params_ts++ m (certparams ++ shape_params) $+ zipWith chunkValues types_grouped_by_params values_grouped_by_params where+ fixAccParams ps =+ first catMaybes $ unzip $ map fixAccParam ps fixAccParam (I.Param attrs pv (I.Acc acc ispace ts u)) = ( Just (I.Param attrs acc $ I.Prim I.Unit), I.Param attrs pv (I.Acc acc ispace ts u) ) fixAccParam p = (Nothing, p) + chunkValues ::+ [[Tree (I.TypeBase I.Shape Uniqueness)]] ->+ [[I.FParam I.SOACS]] ->+ [Tree (I.FParam I.SOACS)]+ chunkValues tss vss =+ concat $ zipWith f tss vss+ where+ f ts vs = zipWith treeLike ts (chunks (map length ts) vs)+ bindingLoopParams :: [E.TypeParam] ->- E.Pat ->+ E.Pat E.ParamType -> [I.Type] -> ([I.FParam I.SOACS] -> [I.FParam I.SOACS] -> InternaliseM a) -> InternaliseM a bindingLoopParams tparams pat ts m = do pat_idents <- flattenPat pat- pat_ts <- internaliseLoopParamType (E.patternStructType pat) ts+ pat_ts <- internaliseLoopParamType (E.patternType pat) ts let shape_params = [I.Param mempty v $ I.Prim I.int64 | E.TypeParamDim v _ <- tparams] shape_subst = M.fromList [(I.paramName p, [I.Var $ I.paramName p]) | p <- shape_params]@@ -78,11 +100,10 @@ bindingFlatPat pat_idents pat_ts $ \valueparams -> I.localScope (I.scopeOfFParams $ shape_params ++ concat valueparams) $ substitutingVars shape_subst $- m shape_params $- concat valueparams+ m shape_params (concat valueparams) bindingLambdaParams ::- [E.Pat] ->+ [E.Pat E.ParamType] -> [I.Type] -> ([I.LParam I.SOACS] -> InternaliseM a) -> InternaliseM a@@ -90,13 +111,16 @@ params_idents <- concat <$> mapM flattenPat params bindingFlatPat params_idents ts $ \params' ->- I.localScope (I.scopeOfLParams $ concat params') $ m $ concat params'+ I.localScope (I.scopeOfLParams $ concat params') $+ m (concat params') +type Params t = [I.Param t]+ processFlatPat :: Show t =>- [(E.Ident, [E.AttrInfo VName])] ->+ [(E.Ident ParamType, [E.AttrInfo VName])] -> [t] ->- InternaliseM ([[I.Param t]], VarSubsts)+ InternaliseM ([Params t], VarSubsts) processFlatPat x y = processFlatPat' [] x y where processFlatPat' pat [] _ = do@@ -105,7 +129,10 @@ processFlatPat' pat ((p, attrs) : rest) ts = do attrs' <- internaliseAttrs attrs (ps, rest_ts) <- handleMapping attrs' ts <$> internaliseBindee p- processFlatPat' ((ps, (E.identName p, map (I.Var . I.paramName) ps)) : pat) rest rest_ts+ processFlatPat'+ ((ps, (E.identName p, map (I.Var . I.paramName) ps)) : pat)+ rest+ rest_ts handleMapping _ ts [] = ([], ts)@@ -113,9 +140,9 @@ let (ps, ts') = handleMapping attrs ts rs in (I.Param attrs r t : ps, ts') handleMapping _ [] _ =- error $ "handleMapping: insufficient identifiers in pattern." ++ show (x, y)+ error $ "handleMapping: insufficient identifiers in pattern.\n" ++ show (x, y) - internaliseBindee :: E.Ident -> InternaliseM [VName]+ internaliseBindee :: E.Ident E.ParamType -> InternaliseM [VName] internaliseBindee bindee = do let name = E.identName bindee case internalisedTypeSize $ E.unInfo $ E.identType bindee of@@ -124,9 +151,9 @@ bindingFlatPat :: Show t =>- [(E.Ident, [E.AttrInfo VName])] ->+ [(E.Ident E.ParamType, [E.AttrInfo VName])] -> [t] ->- ([[I.Param t]] -> InternaliseM a) ->+ ([Params t] -> InternaliseM a) -> InternaliseM a bindingFlatPat idents ts m = do (ps, substs) <- processFlatPat idents ts@@ -134,7 +161,7 @@ m ps -- | Flatten a pattern. Returns a list of identifiers.-flattenPat :: MonadFreshNames m => E.Pat -> m [(E.Ident, [E.AttrInfo VName])]+flattenPat :: MonadFreshNames m => E.Pat (TypeBase Size u) -> m [(E.Ident (TypeBase Size u), [E.AttrInfo VName])] flattenPat = flattenPat' where flattenPat' (E.PatParens p _) =@@ -162,7 +189,7 @@ concat <$> mapM flattenPat' ps stmPat ::- E.Pat ->+ E.Pat E.ParamType -> [I.Type] -> ([VName] -> InternaliseM a) -> InternaliseM a
src/Futhark/Internalise/Defunctionalise.hs view
@@ -15,7 +15,7 @@ import Data.Set qualified as S import Futhark.IR.Pretty () import Futhark.MonadFreshNames-import Futhark.Util (mapAccumLM)+import Futhark.Util (mapAccumLM, nubOrd) import Language.Futhark import Language.Futhark.Traversals import Language.Futhark.TypeChecker.Types (Subst (..), applySubst)@@ -23,30 +23,30 @@ -- | A static value stores additional information about the result of -- defunctionalization of an expression, aside from the residual expression. data StaticVal- = Dynamic PatType+ = Dynamic ParamType | -- | The Env is the lexical closure of the lambda.- LambdaSV Pat StructRetType Exp Env+ LambdaSV (Pat ParamType) ResRetType Exp Env | RecordSV [(Name, StaticVal)] | -- | The constructor that is actually present, plus -- the others that are not.- SumSV Name [StaticVal] [(Name, [PatType])]+ SumSV Name [StaticVal] [(Name, [ParamType])] | -- | The pair is the StaticVal and residual expression of this -- function as a whole, while the second StaticVal is its -- body. (Don't trust this too much, my understanding may have -- holes.) DynamicFun (Exp, StaticVal) StaticVal | IntrinsicSV- | HoleSV PatType SrcLoc+ | HoleSV StructType SrcLoc deriving (Show) --- | The type is Just if this is a polymorphic binding that must be--- instantiated.-data Binding = Binding (Maybe ([VName], StructType)) StaticVal+data Binding = Binding+ { -- | Just if this is a polymorphic binding that must be+ -- instantiated.+ bindingType :: Maybe ([VName], StructType),+ bindingSV :: StaticVal+ } deriving (Show) -bindingSV :: Binding -> StaticVal-bindingSV (Binding _ sv) = sv- -- | Environment mapping variable names to their associated static -- value. type Env = M.Map VName Binding@@ -72,11 +72,11 @@ TypeBase Size als replaceTypeSizes substs = first onDim where- onDim (NamedSize v) =+ onDim (Var v typ loc) = case M.lookup (qualLeaf v) substs of- Just (SubstNamed v') -> NamedSize v'- Just (SubstConst d) -> ConstSize d- Nothing -> NamedSize v+ Just (SubstNamed v') -> Var v' typ loc+ Just (SubstConst d) -> sizeFromInteger (toInteger d) loc+ Nothing -> Var v typ loc onDim d = d replaceStaticValSizes ::@@ -92,7 +92,7 @@ foldl' (flip M.delete) orig_substs $ S.fromList (M.keys closure_env) in LambdaSV- (onAST substs param)+ (fmap (replaceTypeSizes substs) param) (RetType t_dims (replaceTypeSizes substs t)) (onExp substs e) (onEnv orig_substs closure_env) -- intentional@@ -112,9 +112,10 @@ HoleSV t loc where tv substs =- identityMapper- { mapOnPatType = pure . replaceTypeSizes substs,- mapOnStructType = pure . replaceTypeSizes substs,+ ASTMapper+ { mapOnStructType = pure . replaceTypeSizes substs,+ mapOnParamType = pure . replaceTypeSizes substs,+ mapOnResRetType = pure, mapOnExp = pure . onExp substs, mapOnName = pure . onName substs }@@ -132,47 +133,16 @@ Literal (SignedValue (Int64Value (fromIntegral d))) loc Nothing -> Var v (replaceTypeSizes substs <$> t) loc- onExp substs (AppExp (Coerce e te loc) (Info (AppRes t ext))) =- AppExp (Coerce (onExp substs e) te' loc) (Info (AppRes (replaceTypeSizes substs t) ext))- where- te' = onTypeExp substs te- onExp substs (Lambda params e ret (Info (als, RetType t_dims t)) loc) =+ onExp substs (Coerce e te t loc) =+ Coerce (onExp substs e) te (replaceTypeSizes substs <$> t) loc+ onExp substs (Lambda params e ret (Info (RetType t_dims t)) loc) = Lambda- (map (onAST substs) params)+ (map (fmap $ replaceTypeSizes substs) params) (onExp substs e) ret- (Info (als, RetType t_dims (replaceTypeSizes substs t)))+ (Info (RetType t_dims (replaceTypeSizes substs t))) loc- onExp substs e = onAST substs e-- onTypeExpDim substs (SizeExp e loc) = SizeExp (onExp substs e) loc- onTypeExpDim _ (SizeExpAny loc) = SizeExpAny loc-- onTypeArgExp substs (TypeArgExpSize d) =- TypeArgExpSize (onTypeExpDim substs d)- onTypeArgExp substs (TypeArgExpType te) =- TypeArgExpType (onTypeExp substs te)-- onTypeExp substs (TEArray d te loc) =- TEArray (onTypeExpDim substs d) (onTypeExp substs te) loc- onTypeExp substs (TEUnique t loc) =- TEUnique (onTypeExp substs t) loc- onTypeExp substs (TEApply t1 t2 loc) =- TEApply (onTypeExp substs t1) (onTypeArgExp substs t2) loc- onTypeExp substs (TEArrow p t1 t2 loc) =- TEArrow p (onTypeExp substs t1) (onTypeExp substs t2) loc- onTypeExp substs (TETuple ts loc) =- TETuple (map (onTypeExp substs) ts) loc- onTypeExp substs (TERecord ts loc) =- TERecord (map (fmap $ onTypeExp substs) ts) loc- onTypeExp substs (TESum ts loc) =- TESum (map (fmap $ map $ onTypeExp substs) ts) loc- onTypeExp substs (TEDim dims t loc) =- TEDim dims (onTypeExp substs t) loc- onTypeExp substs (TEParens te loc) =- TEParens (onTypeExp substs te) loc- onTypeExp _ (TEVar v loc) =- TEVar v loc+ onExp substs e = runIdentity $ astMap (tv substs) e onEnv substs = M.fromList@@ -184,35 +154,29 @@ (second (replaceTypeSizes substs) <$> t) (replaceStaticValSizes globals substs bsv) - onAST :: ASTMappable x => M.Map VName SizeSubst -> x -> x- onAST substs = runIdentity . astMap (tv substs)- -- | Returns the defunctionalization environment restricted--- to the given set of variable names and types.+-- to the given set of variable names. restrictEnvTo :: FV -> DefM Env-restrictEnvTo (FV m) = asks restrict+restrictEnvTo fv = asks restrict where restrict (globals, env) = M.mapMaybeWithKey keep env where keep k (Binding t sv) = do- guard $ not $ k `S.member` globals- u <- uniqueness <$> M.lookup k m- Just $ Binding t $ restrict' u sv- restrict' Nonunique (Dynamic t) =- Dynamic $ t `setUniqueness` Nonunique- restrict' _ (Dynamic t) =+ guard $ not (k `S.member` globals) && S.member k (fvVars fv)+ Just $ Binding t $ restrict' sv+ restrict' (Dynamic t) = Dynamic t- restrict' u (LambdaSV pat t e env) =- LambdaSV pat t e $ M.map (restrict'' u) env- restrict' u (RecordSV fields) =- RecordSV $ map (fmap $ restrict' u) fields- restrict' u (SumSV c svs fields) =- SumSV c (map (restrict' u) svs) fields- restrict' u (DynamicFun (e, sv1) sv2) =- DynamicFun (e, restrict' u sv1) $ restrict' u sv2- restrict' _ IntrinsicSV = IntrinsicSV- restrict' _ (HoleSV t loc) = HoleSV t loc- restrict'' u (Binding t sv) = Binding t $ restrict' u sv+ restrict' (LambdaSV pat t e env) =+ LambdaSV pat t e $ M.map restrict'' env+ restrict' (RecordSV fields) =+ RecordSV $ map (fmap restrict') fields+ restrict' (SumSV c svs fields) =+ SumSV c (map restrict' svs) fields+ restrict' (DynamicFun (e, sv1) sv2) =+ DynamicFun (e, restrict' sv1) $ restrict' sv2+ restrict' IntrinsicSV = IntrinsicSV+ restrict' (HoleSV t loc) = HoleSV t loc+ restrict'' (Binding t sv) = Binding t $ restrict' sv -- | Defunctionalization monad. The Reader environment tracks both -- the current Env as well as the set of globally defined dynamic@@ -242,6 +206,36 @@ addValBind :: ValBind -> DefM () addValBind vb = modify $ first (vb :) +-- | Create a new top-level value declaration with the given function name,+-- return type, list of parameters, and body expression.+liftValDec :: VName -> ResRetType -> [VName] -> [Pat ParamType] -> Exp -> DefM ()+liftValDec fname (RetType ret_dims ret) dims pats body = addValBind dec+ where+ dims' = map (`TypeParamDim` mempty) dims+ -- FIXME: this pass is still not correctly size-preserving, so+ -- forget those return sizes that we forgot to propagate along+ -- the way. Hopefully the internaliser is conservative and+ -- will insert reshapes...+ bound_here = S.fromList $ dims <> foldMap patNames pats+ mkExt v+ | not $ v `S.member` bound_here = Just v+ mkExt _ = Nothing+ rettype_st = RetType (mapMaybe mkExt (S.toList $ fvVars $ freeInType ret) ++ ret_dims) ret++ dec =+ ValBind+ { valBindEntryPoint = Nothing,+ valBindName = fname,+ valBindRetDecl = Nothing,+ valBindRetType = Info rettype_st,+ valBindTypeParams = dims',+ valBindParams = pats,+ valBindBody = body,+ valBindDoc = Nothing,+ valBindAttrs = mempty,+ valBindLocation = mempty+ }+ -- | Looks up the associated static value for a given name in the environment. lookupVar :: StructType -> VName -> DefM StaticVal lookupVar t x = do@@ -265,21 +259,21 @@ arraySizes (Scalar Arrow {}) = mempty arraySizes (Scalar (Record fields)) = foldMap arraySizes fields arraySizes (Scalar (Sum cs)) = foldMap (foldMap arraySizes) cs-arraySizes (Scalar (TypeVar _ _ _ targs)) =+arraySizes (Scalar (TypeVar _ _ targs)) = mconcat $ map f targs where- f (TypeArgDim (NamedSize d) _) = S.singleton $ qualLeaf d+ f (TypeArgDim (Var d _ _)) = S.singleton $ qualLeaf d f TypeArgDim {} = mempty- f (TypeArgType t _) = arraySizes t+ f (TypeArgType t) = arraySizes t arraySizes (Scalar Prim {}) = mempty-arraySizes (Array _ _ shape t) =+arraySizes (Array _ shape t) = arraySizes (Scalar t) <> foldMap dimName (shapeDims shape) where dimName :: Size -> S.Set VName- dimName (NamedSize qn) = S.singleton $ qualLeaf qn+ dimName (Var qn _ _) = S.singleton $ qualLeaf qn dimName _ = mempty -patternArraySizes :: Pat -> S.Set VName+patternArraySizes :: Pat ParamType -> S.Set VName patternArraySizes = arraySizes . patternStructType data SizeSubst@@ -294,14 +288,14 @@ M.Map VName SizeSubst dimMapping t1 t2 = execState (matchDims f t1 t2) mempty where- f bound d1 (NamedSize d2)+ f bound d1 (Var d2 _ _) | qualLeaf d2 `elem` bound = pure d1- f _ (NamedSize d1) (NamedSize d2) = do+ f _ (Var d1 typ loc) (Var d2 _ _) = do modify $ M.insert (qualLeaf d1) $ SubstNamed d2- pure $ NamedSize d1- f _ (NamedSize d1) (ConstSize d2) = do- modify $ M.insert (qualLeaf d1) $ SubstConst d2- pure $ NamedSize d1+ pure $ Var d1 typ loc+ f _ (Var d1 typ loc) (IntLit d2 _ _) = do+ modify $ M.insert (qualLeaf d1) $ SubstConst $ fromInteger d2+ pure $ Var d1 typ loc f _ d _ = pure d dimMapping' ::@@ -328,11 +322,52 @@ sizesToRename (SumSV _ svs _) = foldMap sizesToRename svs sizesToRename (LambdaSV param _ _ _) =- freeInPat param- <> S.map identName (S.filter couldBeSize $ patIdents param)+ -- We used to rename parameters here, but I don't understand why+ -- that was necessary and it caused some problems.+ fvVars (freeInPat param)++-- | Combine the shape information of types as much as possible. The first+-- argument is the orignal type and the second is the type of the transformed+-- expression. This is necessary since the original type may contain additional+-- information (e.g., shape restrictions) from the user given annotation.+combineTypeShapes ::+ (Monoid as) =>+ TypeBase Size as ->+ TypeBase Size as ->+ TypeBase Size as+combineTypeShapes (Scalar (Record ts1)) (Scalar (Record ts2))+ | M.keys ts1 == M.keys ts2 =+ Scalar $+ Record $+ M.map+ (uncurry combineTypeShapes)+ (M.intersectionWith (,) ts1 ts2)+combineTypeShapes (Scalar (Sum cs1)) (Scalar (Sum cs2))+ | M.keys cs1 == M.keys cs2 =+ Scalar $+ Sum $+ M.map+ (uncurry $ zipWith combineTypeShapes)+ (M.intersectionWith (,) cs1 cs2)+combineTypeShapes (Scalar (Arrow als1 p1 d1 a1 (RetType dims1 b1))) (Scalar (Arrow als2 _p2 _d2 a2 (RetType _ b2))) =+ Scalar $+ Arrow+ (als1 <> als2)+ p1+ d1+ (combineTypeShapes a1 a2)+ (RetType dims1 (combineTypeShapes b1 b2))+combineTypeShapes (Scalar (TypeVar u v targs1)) (Scalar (TypeVar _ _ targs2)) =+ Scalar $ TypeVar u v $ zipWith f targs1 targs2 where- couldBeSize ident =- unInfo (identType ident) == Scalar (Prim (Signed Int64))+ f (TypeArgType t1) (TypeArgType t2) = TypeArgType (combineTypeShapes t1 t2)+ f targ _ = targ+combineTypeShapes (Array u shape1 et1) (Array _ _shape2 et2) =+ arrayOfWithAliases+ u+ shape1+ (combineTypeShapes (setUniqueness (Scalar et1) u) (setUniqueness (Scalar et2) u))+combineTypeShapes _ t = t -- When we instantiate a polymorphic StaticVal, we rename all the -- sizes to avoid name conflicts later on. This is a bit of a hack...@@ -348,7 +383,6 @@ fresh_substs <- mkSubsts . filter (`S.notMember` globals) . S.toList $ S.fromList dims <> sizesToRename sv- let dims' = map (onName fresh_substs) dims isDim k _ = k `elem` dims' dim_substs =@@ -370,9 +404,9 @@ defuncFun :: [VName] ->- [Pat] ->+ [Pat ParamType] -> Exp ->- StructRetType ->+ ResRetType -> SrcLoc -> DefM (Exp, StaticVal) defuncFun tparams pats e0 ret loc = do@@ -383,22 +417,22 @@ [pat'] -> (pat', ret, e0) (pat' : pats') -> ( pat',- RetType [] $ funType pats' ret,- Lambda pats' e0 Nothing (Info (mempty, ret)) loc+ RetType [] $ second (const Nonunique) $ funType pats' ret,+ Lambda pats' e0 Nothing (Info ret) loc ) -- Construct a record literal that closes over the environment of -- the lambda. Closed-over 'DynamicFun's are converted to their -- closure representation. let used =- freeInExp (Lambda pats e0 Nothing (Info (mempty, ret)) loc)+ freeInExp (Lambda pats e0 Nothing (Info ret) loc) `freeWithout` S.fromList tparams used_env <- restrictEnvTo used -- The closure parts that are sizes are proactively turned into size -- parameters. let sizes_of_arrays =- foldMap (arraySizes . toStruct . typeFromSV . bindingSV) used_env+ foldMap (arraySizes . structTypeFromSV . bindingSV) used_env <> patternArraySizes pat notSize = not . (`S.member` sizes_of_arrays) (fields, env) =@@ -420,7 +454,7 @@ ) closureFromDynamicFun (vn, Binding _ sv) = let name = nameFromString $ prettyString vn- tp' = typeFromSV sv+ tp' = structTypeFromSV sv in ( RecordFieldExplicit name (Var (qualName vn) (Info tp') mempty)@@ -432,13 +466,13 @@ -- the associated static value in the defunctionalization monad. defuncExp :: Exp -> DefM (Exp, StaticVal) defuncExp e@Literal {} =- pure (e, Dynamic $ typeOf e)+ pure (e, Dynamic $ toParam Observe $ typeOf e) defuncExp e@IntLit {} =- pure (e, Dynamic $ typeOf e)+ pure (e, Dynamic $ toParam Observe $ typeOf e) defuncExp e@FloatLit {} =- pure (e, Dynamic $ typeOf e)+ pure (e, Dynamic $ toParam Observe $ typeOf e) defuncExp e@StringLit {} =- pure (e, Dynamic $ typeOf e)+ pure (e, Dynamic $ toParam Observe $ typeOf e) defuncExp (Parens e loc) = do (e', sv) <- defuncExp e pure (Parens e' loc, sv)@@ -470,35 +504,36 @@ -- The field may refer to a functional expression, so we get the -- type from the static value and not the one from the AST. _ ->- let tp = Info $ typeFromSV sv+ let tp = Info $ structTypeFromSV sv in pure (RecordFieldImplicit vn tp loc', (baseName vn, sv)) defuncExp (ArrayLit es t@(Info t') loc) = do es' <- mapM defuncExp' es- pure (ArrayLit es' t loc, Dynamic t')+ pure (ArrayLit es' t loc, Dynamic $ toParam Observe t') defuncExp (AppExp (Range e1 me incl loc) res) = do e1' <- defuncExp' e1 me' <- mapM defuncExp' me incl' <- mapM defuncExp' incl pure ( AppExp (Range e1' me' incl' loc) res,- Dynamic $ appResType $ unInfo res+ Dynamic $ toParam Observe $ appResType $ unInfo res ) defuncExp e@(Var qn (Info t) loc) = do sv <- lookupVar (toStruct t) (qualLeaf qn) case sv of- -- If the variable refers to a dynamic function, we return its closure- -- representation (i.e., a record expression capturing the free variables- -- and a 'LambdaSV' static value) instead of the variable itself.- DynamicFun closure _ -> pure closure+ -- If the variable refers to a dynamic function, we eta-expand it+ -- so that we do not have to duplicate its definition.+ DynamicFun {} -> do+ (params, body, ret) <- etaExpand (RetType [] $ toRes Nonunique t) e+ defuncFun [] params body ret mempty -- Intrinsic functions used as variables are eta-expanded, so we -- can get rid of them. IntrinsicSV -> do- (pats, body, tp) <- etaExpand (RetType [] (typeOf e)) e- defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) mempty+ (pats, body, tp) <- etaExpand (RetType [] $ toRes Nonunique t) e+ defuncExp $ Lambda pats body Nothing (Info tp) mempty HoleSV _ hole_loc -> pure (Hole (Info t) hole_loc, sv) _ ->- pure (Var qn (Info (typeFromSV sv)) loc, sv)+ pure (Var qn (Info (structTypeFromSV sv)) loc, sv) defuncExp (Hole (Info t) loc) = pure (Hole (Info t) loc, HoleSV t loc) defuncExp (Ascript e0 tydecl loc)@@ -506,24 +541,23 @@ (e0', sv) <- defuncExp e0 pure (Ascript e0' tydecl loc, sv) | otherwise = defuncExp e0-defuncExp (AppExp (Coerce e0 tydecl loc) res)+defuncExp (Coerce e0 tydecl t loc) | orderZero (typeOf e0) = do (e0', sv) <- defuncExp e0- pure (AppExp (Coerce e0' tydecl loc) res, sv)+ pure (Coerce e0' tydecl t loc, sv) | otherwise = defuncExp e0 defuncExp (AppExp (LetPat sizes pat e1 e2 loc) (Info (AppRes t retext))) = do (e1', sv1) <- defuncExp e1- let env = alwaysMatchPatSV pat sv1- pat' = updatePat pat sv1+ let env = alwaysMatchPatSV (fmap (toParam Observe) pat) sv1+ pat' = updatePat (fmap (toParam Observe) pat) sv1 (e2', sv2) <- localEnv env $ defuncExp e2 -- To maintain any sizes going out of scope, we need to compute the -- old size substitution induced by retext and also apply it to the -- newly computed body type. let mapping = dimMapping' (typeOf e2) t- subst v = fromMaybe v $ M.lookup v mapping- mapper = identityMapper {mapOnName = pure . subst}- t' = first (runIdentity . astMap mapper) $ typeOf e2'- pure (AppExp (LetPat sizes pat' e1' e2' loc) (Info (AppRes t' retext)), sv2)+ subst v = ExpSubst . flip sizeFromName mempty . qualName <$> M.lookup v mapping+ t' = applySubst subst $ typeOf e2'+ pure (AppExp (LetPat sizes (fmap toStruct pat') e1' e2' loc) (Info (AppRes t' retext)), sv2) defuncExp (AppExp (LetFun vn _ _ _) _) = error $ "defuncExp: Unexpected LetFun: " ++ show vn defuncExp (AppExp (If e1 e2 e3 loc) res) = do@@ -539,7 +573,7 @@ defuncExp (Not e0 loc) = do (e0', sv) <- defuncExp e0 pure (Not e0' loc, sv)-defuncExp (Lambda pats e0 _ (Info (_, ret)) loc) =+defuncExp (Lambda pats e0 _ (Info ret) loc) = defuncFun [] pats e0 ret loc -- Operator sections are expected to be converted to lambda-expressions -- by the monomorphizer, so they should no longer occur at this point.@@ -557,7 +591,7 @@ pure (For v e2', envFromIdent v) ForIn pat2 e2 -> do e2' <- defuncExp' e2- pure (ForIn pat2 e2', envFromPat pat2)+ pure (ForIn pat2 e2', envFromPat $ fmap (toParam Observe) pat2) While e2 -> do e2' <- localEnv env1 $ defuncExp' e2 pure (While e2', mempty)@@ -565,22 +599,23 @@ pure (AppExp (DoLoop sparams pat e1' form' e3' loc) res, sv) where envFromIdent (Ident vn (Info tp) _) =- M.singleton vn $ Binding Nothing $ Dynamic tp+ M.singleton vn $ Binding Nothing $ Dynamic $ toParam Observe tp defuncExp e@(AppExp BinOp {} _) = error $ "defuncExp: unexpected binary operator: " ++ prettyString e defuncExp (Project vn e0 tp@(Info tp') loc) = do (e0', sv0) <- defuncExp e0 case sv0 of RecordSV svs -> case lookup vn svs of- Just sv -> pure (Project vn e0' (Info $ typeFromSV sv) loc, sv)+ Just sv -> pure (Project vn e0' (Info $ structTypeFromSV sv) loc, sv) Nothing -> error "Invalid record projection."- Dynamic _ -> pure (Project vn e0' tp loc, Dynamic tp')+ Dynamic _ -> pure (Project vn e0' tp loc, Dynamic $ toParam Observe tp') HoleSV _ hloc -> pure (Project vn e0' tp loc, HoleSV tp' hloc) _ -> error $ "Projection of an expression with static value " ++ show sv0 defuncExp (AppExp (LetWith id1 id2 idxs e1 body loc) res) = do e1' <- defuncExp' e1 idxs' <- mapM defuncDimIndex idxs- let id1_binding = Binding Nothing $ Dynamic $ unInfo $ identType id1+ let id1_binding =+ Binding Nothing $ Dynamic $ toParam Observe $ unInfo $ identType id1 (body', sv) <- localEnv (M.singleton (identName id1) id1_binding) $ defuncExp body@@ -590,7 +625,7 @@ idxs' <- mapM defuncDimIndex idxs pure ( AppExp (Index e0' idxs' loc) res,- Dynamic $ typeOf expr+ Dynamic $ toParam Observe $ typeOf expr ) defuncExp (Update e1 idxs e2 loc) = do (e1', sv) <- defuncExp e1@@ -606,7 +641,7 @@ (e2', sv2) <- defuncExp e2 let sv = staticField sv1 sv2 fs pure- ( RecordUpdate e1' fs e2' (Info $ typeFromSV sv1) loc,+ ( RecordUpdate e1' fs e2' (Info $ structTypeFromSV sv1) loc, sv ) where@@ -628,15 +663,15 @@ let sv = SumSV name svs $ M.toList $- name `M.delete` M.map (map defuncType) all_fs- sum_t' = combineTypeShapes sum_t (typeFromSV sv)+ name `M.delete` M.map (map (toParam Observe . defuncType)) all_fs+ sum_t' = combineTypeShapes sum_t (structTypeFromSV sv) pure (Constr name es' (Info sum_t') loc, sv) where defuncType :: Monoid als => TypeBase Size als -> TypeBase Size als- defuncType (Array as u shape t) = Array as u shape (defuncScalar t)+ defuncType (Array u shape t) = Array u shape (defuncScalar t) defuncType (Scalar t) = Scalar $ defuncScalar t defuncScalar ::@@ -647,7 +682,7 @@ defuncScalar Arrow {} = Record mempty defuncScalar (Sum fs) = Sum $ M.map (map defuncType) fs defuncScalar (Prim t) = Prim t- defuncScalar (TypeVar as u tn targs) = TypeVar as u tn targs+ defuncScalar (TypeVar u tn targs) = TypeVar u tn targs defuncExp (Constr name _ (Info t) loc) = error $ "Constructor "@@ -675,11 +710,11 @@ defuncCase :: StaticVal -> Case -> DefM (Maybe (Case, StaticVal)) defuncCase sv (CasePat p e loc) = do- let p' = updatePat p sv- case matchPatSV p sv of+ let p' = updatePat (fmap (toParam Observe) p) sv+ case matchPatSV (fmap (toParam Observe) p) sv of Just env -> do (e', sv') <- localEnv env $ defuncExp e- pure $ Just (CasePat p' e' loc, sv')+ pure $ Just (CasePat (fmap toStruct p') e' loc, sv') Nothing -> pure Nothing @@ -698,30 +733,30 @@ pure $ Lambda params e0' decl tp loc defuncSoacExp e | Scalar Arrow {} <- typeOf e = do- (pats, body, tp) <- etaExpand (RetType [] (typeOf e)) e+ (pats, body, tp) <- etaExpand (RetType [] $ toRes Nonunique $ typeOf e) e let env = foldMap envFromPat pats body' <- localEnv env $ defuncExp' body- pure $ Lambda pats body' Nothing (Info (mempty, tp)) mempty+ pure $ Lambda pats body' Nothing (Info tp) mempty | otherwise = defuncExp' e -etaExpand :: PatRetType -> Exp -> DefM ([Pat], Exp, StructRetType)+etaExpand :: ResRetType -> Exp -> DefM ([Pat ParamType], Exp, ResRetType) etaExpand e_t e = do let (ps, ret) = getType e_t -- Some careful hackery to avoid duplicate names.- (_, (pats, vars)) <- second unzip <$> mapAccumLM f [] ps+ (_, (params, vars)) <- second unzip <$> mapAccumLM f [] ps -- Important that we synthesize new existential names and substitute -- them into the (body) return type. ext' <- mapM newName $ retDims ret let extsubst = M.fromList . zip (retDims ret) $- map (SizeSubst . NamedSize . qualName) ext'+ map (ExpSubst . flip sizeFromName mempty . qualName) ext' ret' = applySubst (`M.lookup` extsubst) ret e' = mkApply e- (zip3 (map (diet . snd . snd) ps) (repeat Nothing) vars)- (AppRes (retType ret') ext')- pure (pats, e', second (const ()) ret)+ (zip3 (map (fst . snd) ps) (repeat Nothing) vars)+ (AppRes (toStruct $ retType ret') ext')+ pure (params, e', ret) where getType (RetType _ (Scalar (Arrow _ p d t1 t2))) = let (ps, r) = getType t2@@ -729,18 +764,14 @@ getType t = ([], t) f prev (p, (d, t)) = do- let t' =- fromStruct t- `setUniqueness` case d of- Consume -> Unique- Observe -> Nonunique+ let t' = second (const d) t x <- case p of Named x | x `notElem` prev -> pure x- _ -> newNameFromString "x"+ _ -> newNameFromString "eta_p" pure ( x : prev, ( Id x (Info t') mempty,- Var (qualName x) (Info t') mempty+ Var (qualName x) (Info $ toStruct t') mempty ) ) @@ -756,70 +787,76 @@ -- that have order 0 types (i.e., non-functional). defuncLet :: [VName] ->- [Pat] ->+ [Pat ParamType] -> Exp ->- StructRetType ->- DefM ([VName], [Pat], Exp, StaticVal)+ ResRetType ->+ DefM ([VName], [Pat ParamType], Exp, StaticVal, ResType) defuncLet dims ps@(pat : pats) body (RetType ret_dims rettype) | patternOrderZero pat = do- let bound_by_pat = (`S.member` freeInPat pat)+ let bound_by_pat = (`S.member` fvVars (freeInPat pat)) -- Take care to not include more size parameters than necessary. (pat_dims, rest_dims) = partition bound_by_pat dims env = envFromPat pat <> envFromDimNames pat_dims- (rest_dims', pats', body', sv) <-+ (rest_dims', pats', body', sv, sv_t) <- localEnv env $ defuncLet rest_dims pats body $ RetType ret_dims rettype closure <- defuncFun dims ps body (RetType ret_dims rettype) mempty pure ( pat_dims ++ rest_dims', pat : pats', body',- DynamicFun closure sv+ DynamicFun closure sv,+ sv_t ) | otherwise = do (e, sv) <- defuncFun dims ps body (RetType ret_dims rettype) mempty- pure ([], [], e, sv)+ pure ([], [], e, sv, resTypeFromSV sv) defuncLet _ [] body (RetType _ rettype) = do (body', sv) <- defuncExp body- pure ([], [], body', imposeType sv rettype)+ pure+ ( [],+ [],+ body',+ imposeType sv $ resToParam rettype,+ resTypeFromSV sv+ ) where imposeType Dynamic {} t =- Dynamic $ fromStruct t+ Dynamic t imposeType (RecordSV fs1) (Scalar (Record fs2)) = RecordSV $ M.toList $ M.intersectionWith imposeType (M.fromList fs1) fs2 imposeType sv _ = sv -sizesForAll :: MonadFreshNames m => S.Set VName -> [Pat] -> m ([VName], [Pat])-sizesForAll bound_sizes params = do- (params', sizes) <- runStateT (mapM (astMap tv) params) mempty- pure (S.toList sizes, params')+instAnySizes :: MonadFreshNames m => [Pat ParamType] -> m [Pat ParamType]+instAnySizes = traverse $ traverse $ bitraverse onDim pure where- bound = bound_sizes <> foldMap patNames params- tv = identityMapper {mapOnPatType = bitraverse onDim pure}- onDim (AnySize (Just v)) = do- modify $ S.insert v- pure $ NamedSize $ qualName v- onDim (AnySize Nothing) = do- v <- lift $ newVName "size"- modify $ S.insert v- pure $ NamedSize $ qualName v- onDim (NamedSize d) = do- unless (qualLeaf d `S.member` bound) $- modify $- S.insert $- qualLeaf d- pure $ NamedSize d+ onDim d+ | d == anySize = do+ v <- newVName "size"+ pure $ sizeFromName (qualName v) mempty onDim d = pure d -unRetType :: StructRetType -> StructType-unRetType (RetType [] t) = t-unRetType (RetType ext t) = first onDim t+unboundSizes :: S.Set VName -> [Pat ParamType] -> [VName]+unboundSizes bound_sizes params = nubOrd $ execState (f params) [] where- onDim (NamedSize d) | qualLeaf d `elem` ext = AnySize Nothing- onDim d = d+ f = traverse $ traverse $ bitraverse onDim pure+ bound = bound_sizes <> S.fromList (foldMap patNames params)+ onDim (Var d typ loc) = do+ unless (qualLeaf d `S.member` bound) $ modify (qualLeaf d :)+ pure $ Var d typ loc+ onDim d = pure d +unRetType :: ResRetType -> DefM AppRes+unRetType (RetType [] t) = pure $ AppRes (toStruct t) []+unRetType (RetType ext t) = do+ ext' <- mapM newName ext+ let extsubst =+ M.fromList . zip ext $+ map (ExpSubst . flip sizeFromName mempty . qualName) ext'+ pure $ AppRes (applySubst (`M.lookup` extsubst) $ toStruct t) ext'+ defuncApplyFunction :: Exp -> Int -> DefM (Exp, StaticVal) defuncApplyFunction e@(Var qn (Info t) loc) num_args = do- let (argtypes, _) = unfoldFunType t+ let (argtypes, rettype) = unfoldFunType t sv <- lookupVar (toStruct t) (qualLeaf qn) case sv of@@ -827,12 +864,16 @@ | fullyApplied sv num_args -> do -- We still need to update the types in case the dynamic -- function returns a higher-order term.- let (argtypes', rettype) = dynamicFunType sv argtypes- pure (Var qn (Info (foldFunType argtypes' $ RetType [] rettype)) loc, sv)+ let (argtypes', rettype') = dynamicFunType sv argtypes+ pure (Var qn (Info (foldFunType argtypes' $ RetType [] rettype')) loc, sv)+ | all orderZero argtypes,+ orderZero rettype -> do+ (params, body, ret) <- etaExpand (RetType [] $ toRes Nonunique t) e+ defuncFun [] params body ret mempty | otherwise -> do fname <- newVName $ "dyn_" <> baseString (qualLeaf qn) let (pats, e0, sv') = liftDynFun (prettyString qn) sv num_args- (argtypes', rettype) = dynamicFunType sv' argtypes+ (argtypes', rettype') = dynamicFunType sv' argtypes dims' = mempty -- Ensure that no parameter sizes are AnySize. The internaliser@@ -840,18 +881,18 @@ -- first-order. globals <- asks fst let bound_sizes = S.fromList dims' <> globals- (missing_dims, pats') <- sizesForAll bound_sizes pats+ pats' <- instAnySizes pats - liftValDec fname (RetType [] $ toStruct rettype) (dims' ++ missing_dims) pats' e0+ liftValDec fname (RetType [] rettype') (dims' ++ unboundSizes bound_sizes pats') pats' e0 pure ( Var (qualName fname)- (Info (foldFunType argtypes' $ RetType [] $ fromStruct rettype))+ (Info (foldFunType argtypes' $ RetType [] rettype')) loc, sv' ) IntrinsicSV -> pure (e, IntrinsicSV)- _ -> pure (Var qn (Info (typeFromSV sv)) loc, sv)+ _ -> pure (Var qn (Info (structTypeFromSV sv)) loc, sv) defuncApplyFunction e _ = defuncExp e -- Embed some information about the original function@@ -867,9 +908,9 @@ defuncApplyArg :: String -> (Exp, StaticVal) ->- (((Diet, Maybe VName), Exp), [(Diet, StructType)]) ->+ (((Diet, Maybe VName), Exp), [ParamType]) -> DefM (Exp, StaticVal)-defuncApplyArg fname_s (f', f_sv@(LambdaSV pat lam_e_t lam_e closure_env)) (((d, argext), arg), _) = do+defuncApplyArg fname_s (f', LambdaSV pat lam_e_t lam_e closure_env) (((d, argext), arg), _) = do (arg', arg_sv) <- defuncExp arg let env' = alwaysMatchPatSV pat arg_sv dims = mempty@@ -887,15 +928,12 @@ -- of the lifted function, but this is ultimately all a sham -- and a hack. There is some piece we're missing. let params = [closure_pat, pat']- params_for_rettype = params ++ svParams f_sv ++ svParams arg_sv- svParams (LambdaSV sv_pat _ _ _) = [sv_pat]- svParams _ = []- lifted_rettype = buildRetType closure_env params_for_rettype (unRetType lam_e_t) $ typeOf lam_e'+ lifted_rettype =+ RetType (retDims lam_e_t) $+ combineTypeShapes (retType lam_e_t) (resTypeFromSV sv) already_bound =- globals- <> S.fromList dims- <> S.map identName (foldMap patIdents params)+ globals <> S.fromList (dims <> foldMap patNames params) more_dims = S.toList $@@ -906,22 +944,21 @@ -- expects this. This is easy, because they are all -- first-order. let bound_sizes = S.fromList (dims <> more_dims) <> globals- (missing_dims, params') <- sizesForAll bound_sizes params+ params' <- instAnySizes params fname <- newNameFromString fname_s liftValDec fname- (RetType [] $ toStruct lifted_rettype)- (dims ++ more_dims ++ missing_dims)+ lifted_rettype+ (dims ++ more_dims ++ unboundSizes bound_sizes params') params' lam_e' let f_t = toStruct $ typeOf f' arg_t = toStruct $ typeOf arg'- d1 = Observe- fname_t = foldFunType [(d1, f_t), (d, arg_t)] $ RetType [] lifted_rettype+ fname_t = foldFunType [toParam Observe f_t, toParam d arg_t] lifted_rettype fname' = Var (qualName fname) (Info fname_t) (srclocOf arg)- callret = AppRes lifted_rettype []+ callret <- unRetType lifted_rettype pure ( mkApply fname' [(Observe, Nothing, f'), (Observe, argext, arg')] callret,@@ -977,10 +1014,10 @@ -- immediately any time we encounter a non-fully-applied -- intrinsic? if null $ fst $ unfoldFunType $ appResType appres- then pure (e', Dynamic $ appResType appres)+ then pure (e', Dynamic $ toParam Observe $ appResType appres) else do- (pats, body, tp) <- etaExpand (RetType [] (typeOf e')) e'- defuncExp $ Lambda pats body Nothing (Info (mempty, tp)) mempty+ (pats, body, tp) <- etaExpand (RetType [] $ toRes Nonunique $ typeOf e') e'+ defuncExp $ Lambda pats body Nothing (Info tp) mempty -- | Check if a 'StaticVal' and a given application depth corresponds -- to a fully applied dynamic function.@@ -994,7 +1031,7 @@ -- dimensions, a list of parameters, a function body, and the -- appropriate static value for applying the function at the given -- depth of partial application.-liftDynFun :: String -> StaticVal -> Int -> ([Pat], Exp, StaticVal)+liftDynFun :: String -> StaticVal -> Int -> ([Pat ParamType], Exp, StaticVal) liftDynFun _ (DynamicFun (e, sv) _) 0 = ([], e, sv) liftDynFun s (DynamicFun clsr@(_, LambdaSV pat _ _ _) sv) d | d > 0 =@@ -1010,7 +1047,7 @@ -- | Converts a pattern to an environment that binds the individual names of the -- pattern to their corresponding types wrapped in a 'Dynamic' static value.-envFromPat :: Pat -> Env+envFromPat :: Pat ParamType -> Env envFromPat pat = case pat of TuplePat ps _ -> foldMap envFromPat ps RecordPat fs _ -> foldMap (envFromPat . snd) fs@@ -1027,84 +1064,22 @@ where d = Binding Nothing $ Dynamic $ Scalar $ Prim $ Signed Int64 --- | Create a new top-level value declaration with the given function name,--- return type, list of parameters, and body expression.-liftValDec :: VName -> StructRetType -> [VName] -> [Pat] -> Exp -> DefM ()-liftValDec fname (RetType ret_dims ret) dims pats body = addValBind dec- where- dims' = map (`TypeParamDim` mempty) dims- -- FIXME: this pass is still not correctly size-preserving, so- -- forget those return sizes that we forgot to propagate along- -- the way. Hopefully the internaliser is conservative and- -- will insert reshapes...- bound_here = S.fromList dims <> S.map identName (foldMap patIdents pats)- mkExt v- | not $ v `S.member` bound_here = Just v- mkExt _ = Nothing- rettype_st = RetType (mapMaybe mkExt (S.toList (freeInType ret)) ++ ret_dims) ret-- dec =- ValBind- { valBindEntryPoint = Nothing,- valBindName = fname,- valBindRetDecl = Nothing,- valBindRetType = Info rettype_st,- valBindTypeParams = dims',- valBindParams = pats,- valBindBody = body,- valBindDoc = Nothing,- valBindAttrs = mempty,- valBindLocation = mempty- }- -- | Given a closure environment, construct a record pattern that -- binds the closed over variables. Insert wildcard for any patterns -- that would otherwise clash with size parameters.-buildEnvPat :: [VName] -> Env -> Pat+buildEnvPat :: [VName] -> Env -> Pat ParamType buildEnvPat sizes env = RecordPat (map buildField $ M.toList env) mempty where buildField (vn, Binding _ sv) = ( nameFromString (prettyString vn), if vn `elem` sizes- then Wildcard (Info $ typeFromSV sv) mempty- else Id vn (Info $ typeFromSV sv) mempty+ then Wildcard (Info $ paramTypeFromSV sv) mempty+ else Id vn (Info $ paramTypeFromSV sv) mempty ) --- | Given a closure environment pattern and the type of a term,--- construct the type of that term, where uniqueness is set to--- `Nonunique` for those arrays that are bound in the environment or--- pattern (except if they are unique there). This ensures that a--- lifted function can create unique arrays as long as they do not--- alias any of its parameters. XXX: it is not clear that this is a--- sufficient property, unfortunately.-buildRetType :: Env -> [Pat] -> StructType -> PatType -> PatType-buildRetType env pats = comb- where- bound =- S.fromList (M.keys env) <> S.map identName (foldMap patIdents pats)- boundAsUnique v =- maybe False (unique . unInfo . identType) $- find ((== v) . identName) $- S.toList $- foldMap patIdents pats- problematic v = (v `S.member` bound) && not (boundAsUnique v)- comb (Scalar (Record fs_annot)) (Scalar (Record fs_got)) =- Scalar $ Record $ M.intersectionWith comb fs_annot fs_got- comb (Scalar (Sum cs_annot)) (Scalar (Sum cs_got)) =- Scalar $ Sum $ M.intersectionWith (zipWith comb) cs_annot cs_got- comb (Scalar Arrow {}) t =- descend t- comb got et =- descend $ fromStruct got `setAliases` aliases et-- descend t@Array {}- | any (problematic . aliasVar) (aliases t) = t `setUniqueness` Nonunique- descend (Scalar (Record t)) = Scalar $ Record $ fmap descend t- descend t = t- -- | Compute the corresponding type for the *representation* of a -- given static value (not the original possibly higher-order value).-typeFromSV :: StaticVal -> PatType+typeFromSV :: StaticVal -> ParamType typeFromSV (Dynamic tp) = tp typeFromSV (LambdaSV _ _ _ env) =@@ -1120,24 +1095,33 @@ let svs' = map typeFromSV svs in Scalar $ Sum $ M.insert name svs' $ M.fromList fields typeFromSV (HoleSV t _) =- t+ toParam Observe t typeFromSV IntrinsicSV = error "Tried to get the type from the static value of an intrinsic." +resTypeFromSV :: StaticVal -> ResType+resTypeFromSV = paramToRes . typeFromSV++structTypeFromSV :: StaticVal -> StructType+structTypeFromSV = toStruct . typeFromSV++paramTypeFromSV :: StaticVal -> ParamType+paramTypeFromSV = typeFromSV+ -- | Construct the type for a fully-applied dynamic function from its -- static value and the original types of its arguments.-dynamicFunType :: StaticVal -> [(Diet, StructType)] -> ([(Diet, PatType)], PatType)+dynamicFunType :: StaticVal -> [ParamType] -> ([ParamType], ResType) dynamicFunType (DynamicFun _ sv) (p : ps) = let (ps', ret) = dynamicFunType sv ps- in (second fromStruct p : ps', ret)-dynamicFunType sv _ = ([], typeFromSV sv)+ in (p : ps', ret)+dynamicFunType sv _ = ([], resTypeFromSV sv) -- | Match a pattern with its static value. Returns an environment -- with the identifier components of the pattern mapped to the -- corresponding subcomponents of the static value. If this function -- returns 'Nothing', then it corresponds to an unmatchable case. -- These should only occur for 'Match' expressions.-matchPatSV :: Pat -> StaticVal -> Maybe Env+matchPatSV :: Pat ParamType -> StaticVal -> Maybe Env matchPatSV (TuplePat ps _) (RecordSV ls) = mconcat <$> zipWithM (\p (_, sv) -> matchPatSV p sv) ps ls matchPatSV (RecordPat ps _) (RecordSV ls)@@ -1157,7 +1141,7 @@ else dim_env <> M.singleton vn (Binding Nothing sv) where dim_env =- M.fromList $ map (,i64) $ S.toList $ freeInType t+ M.fromList $ map (,i64) $ S.toList $ fvVars $ freeInType t i64 = Binding Nothing $ Dynamic $ Scalar $ Prim $ Signed Int64 matchPatSV (Wildcard _ _) _ = pure mempty matchPatSV (PatAscription pat _ _) sv = matchPatSV pat sv@@ -1178,7 +1162,7 @@ | otherwise = error $ "matchPatSV: missing constructor in type: " ++ prettyString c1 matchPatSV pat (Dynamic t) = matchPatSV pat $ svFromType t-matchPatSV pat (HoleSV t _) = matchPatSV pat $ svFromType t+matchPatSV pat (HoleSV t _) = matchPatSV pat $ svFromType $ toParam Observe t matchPatSV pat sv = error $ "Tried to match pattern\n"@@ -1186,14 +1170,14 @@ ++ "\n with static value\n" ++ show sv -alwaysMatchPatSV :: Pat -> StaticVal -> Env+alwaysMatchPatSV :: Pat ParamType -> StaticVal -> Env alwaysMatchPatSV pat sv = fromMaybe bad $ matchPatSV pat sv where bad = error $ unlines [prettyString pat, "cannot match StaticVal", show sv] -- | Given a pattern and the static value for the defunctionalized argument, -- update the pattern to reflect the changes in the types.-updatePat :: Pat -> StaticVal -> Pat+updatePat :: Pat ParamType -> StaticVal -> Pat ParamType updatePat (TuplePat ps loc) (RecordSV svs) = TuplePat (zipWith updatePat ps $ map snd svs) loc updatePat (RecordPat ps loc) (RecordSV svs)@@ -1207,7 +1191,7 @@ updatePat (PatAttr attr pat loc) sv = PatAttr attr (updatePat pat sv) loc updatePat (Id vn (Info tp) loc) sv =- Id vn (Info $ comb tp (typeFromSV sv `setUniqueness` Nonunique)) loc+ Id vn (Info $ comb tp $ paramTypeFromSV sv) loc where -- Preserve any original zeroth-order types. comb (Scalar Arrow {}) t2 = t2@@ -1218,20 +1202,20 @@ comb t1 _ = t1 -- t1 must be array or prim. updatePat pat@(Wildcard (Info tp) loc) sv | orderZero tp = pat- | otherwise = Wildcard (Info $ typeFromSV sv) loc+ | otherwise = Wildcard (Info $ paramTypeFromSV sv) loc updatePat (PatAscription pat _ _) sv = updatePat pat sv updatePat p@PatLit {} _ = p updatePat pat@(PatConstr c1 (Info t) ps loc) sv@(SumSV _ svs _) | orderZero t = pat- | otherwise = PatConstr c1 (Info t') ps' loc+ | otherwise = PatConstr c1 (Info $ toParam Observe t') ps' loc where- t' = typeFromSV sv `setUniqueness` Nonunique+ t' = resTypeFromSV sv ps' = zipWith updatePat ps svs updatePat (PatConstr c1 _ ps loc) (Dynamic t) =- PatConstr c1 (Info t) ps loc+ PatConstr c1 (Info $ toParam Observe t) ps loc updatePat pat (Dynamic t) = updatePat pat (svFromType t)-updatePat pat (HoleSV t _) = updatePat pat (svFromType t)+updatePat pat (HoleSV t _) = updatePat pat (svFromType $ toParam Observe t) updatePat pat sv = error $ "Tried to update pattern\n"@@ -1242,7 +1226,7 @@ -- | Convert a record (or tuple) type to a record static value. This -- is used for "unwrapping" tuples and records that are nested in -- 'Dynamic' static values.-svFromType :: PatType -> StaticVal+svFromType :: ParamType -> StaticVal svFromType (Scalar (Record fs)) = RecordSV . M.toList $ M.map svFromType fs svFromType t = Dynamic t @@ -1273,18 +1257,14 @@ show name ++ " has type parameters, " ++ "but the defunctionaliser expects a monomorphic input program."- (tparams', params', body', sv) <-+ (tparams', params', body', sv, sv_t) <- defuncLet (map typeParamName tparams) params body $ RetType ret_dims rettype globals <- asks fst- let bound_sizes = foldMap patNames params' <> S.fromList tparams' <> globals- rettype' =- -- FIXME: dubious that we cannot assume that all sizes in the- -- body are in scope. This is because when we insert- -- applications of lifted functions, we don't properly update- -- the types in the return type annotation.- combineTypeShapes rettype $ first (anyDimIfNotBound bound_sizes) $ toStruct $ typeOf body'- ret_dims' = filter (`S.member` freeInType rettype') ret_dims- (missing_dims, params'') <- sizesForAll bound_sizes params'+ let bound_sizes = S.fromList (foldMap patNames params') <> S.fromList tparams' <> globals+ params'' <- instAnySizes params'+ let rettype' = combineTypeShapes rettype sv_t+ tparams'' = tparams' ++ unboundSizes bound_sizes params''+ ret_dims' = filter (`notElem` bound_sizes) $ S.toList $ fvVars $ freeInType rettype' pure ( valbind@@ -1294,8 +1274,7 @@ if null params' then RetType ret_dims' $ rettype' `setUniqueness` Nonunique else RetType ret_dims' rettype',- valBindTypeParams =- map (`TypeParamDim` mempty) $ tparams' ++ missing_dims,+ valBindTypeParams = map (`TypeParamDim` mempty) tparams'', valBindParams = params'', valBindBody = body' },@@ -1304,10 +1283,6 @@ (Just (first (map typeParamName) (valBindTypeScheme valbind))) sv )- where- anyDimIfNotBound bound_sizes (NamedSize v)- | qualLeaf v `S.notMember` bound_sizes = AnySize $ Just $ qualLeaf v- anyDimIfNotBound _ d = d -- | Defunctionalize a list of top-level declarations. defuncVals :: [ValBind] -> DefM ()
src/Futhark/Internalise/Defunctorise.hs view
@@ -250,9 +250,8 @@ mapOnName = \v -> pure $ qualLeaf $ fst $ lookupSubstInScope (qualName v) scope, mapOnStructType = astMap (substituter scope),- mapOnPatType = astMap (substituter scope),- mapOnStructRetType = astMap (substituter scope),- mapOnPatRetType = astMap (substituter scope)+ mapOnParamType = astMap (substituter scope),+ mapOnResRetType = astMap (substituter scope) } onExp scope e = -- One expression is tricky, because it interacts with scoping rules.@@ -270,6 +269,9 @@ transformStructType :: StructType -> TransformM StructType transformStructType = transformNames +transformResType :: ResType -> TransformM ResType+transformResType = transformNames+ transformExp :: Exp -> TransformM Exp transformExp = transformNames @@ -287,7 +289,7 @@ entry' <- traverse (traverse transformEntry) entry name' <- transformName name tdecl' <- traverse transformTypeExp tdecl- t' <- transformStructType t+ t' <- transformResType t e' <- transformExp e tparams' <- traverse transformNames tparams params' <- traverse transformNames params
src/Futhark/Internalise/Entry.hs view
@@ -65,7 +65,12 @@ runGenOpaque = flip runState mempty addType :: Name -> I.OpaqueType -> GenOpaque ()-addType s t = modify (<> I.OpaqueTypes [(s, t)])+addType name t = modify $ \(I.OpaqueTypes ts) ->+ case find ((== name) . fst) ts of+ Just (_, t')+ | t /= t' ->+ error $ "Duplicate definition of entry point type " <> E.prettyString name+ _ -> I.OpaqueTypes ts <> I.OpaqueTypes [(name, t)] isRecord :: VisibleTypes -> E.TypeExp E.Info VName -> Maybe (M.Map Name (E.TypeExp E.Info VName)) isRecord _ (E.TERecord fs _) = Just $ M.fromList fs@@ -109,13 +114,13 @@ | E.Scalar (E.Prim E.Unsigned {}) <- E.entryType t, [I.Prim ts0] <- ts = pure (u, I.TypeTransparent $ I.ValueType I.Unsigned (I.Rank 0) ts0)- | E.Array _ _ _ (E.Prim E.Unsigned {}) <- E.entryType t,+ | E.Array _ _ (E.Prim E.Unsigned {}) <- E.entryType t, [I.Array ts0 r _] <- ts = pure (u, I.TypeTransparent $ I.ValueType I.Unsigned r ts0) | E.Scalar E.Prim {} <- E.entryType t, [I.Prim ts0] <- ts = pure (u, I.TypeTransparent $ I.ValueType I.Signed (I.Rank 0) ts0)- | E.Array _ _ _ E.Prim {} <- E.entryType t,+ | E.Array _ _ E.Prim {} <- E.entryType t, [I.Array ts0 r _] <- ts = pure (u, I.TypeTransparent $ I.ValueType I.Signed r ts0) | otherwise = do
src/Futhark/Internalise/Exps.hs view
@@ -7,6 +7,8 @@ import Control.Monad import Control.Monad.Reader+import Data.Bifunctor+import Data.Foldable (toList) import Data.List (elemIndex, find, intercalate, intersperse, transpose) import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE@@ -40,31 +42,31 @@ internaliseFunName :: VName -> Name internaliseFunName = nameFromString . prettyString +shiftRetAls :: Int -> RetAls -> RetAls+shiftRetAls d (RetAls pals rals) = RetAls pals $ map (+ d) rals+ internaliseValBind :: VisibleTypes -> E.ValBind -> InternaliseM ()-internaliseValBind types fb@(E.ValBind entry fname retdecl (Info rettype) tparams params body _ attrs loc) = do+internaliseValBind types fb@(E.ValBind entry fname _ (Info rettype) tparams params body _ attrs loc) = do bindingFParams tparams params $ \shapeparams params' -> do let shapenames = map I.paramName shapeparams-- msg <- case retdecl of- Just dt ->- errorMsg- . ("Function return value does not match shape of type " :)- <$> typeExpForError dt- Nothing -> pure $ errorMsg ["Function return value does not match shape of declared return type."]+ all_params = map pure shapeparams ++ concat params'+ msg = errorMsg ["Function return value does not match shape of declared return type."] (body', rettype') <- buildBody $ do body_res <- internaliseExp (baseString fname <> "_res") body- rettype' <-- zeroExts . internaliseReturnType rettype <$> mapM subExpType body_res+ (rettype', retals) <-+ first zeroExts . unzip . internaliseReturnType (map (fmap paramDeclType) all_params) rettype+ <$> mapM subExpType body_res+ body_res' <- ensureResultExtShape msg loc (map I.fromDecl rettype') $ subExpsRes body_res+ let num_ctx = length (shapeContext rettype') pure ( body_res',- replicate (length (shapeContext rettype')) (I.Prim int64) ++ rettype'+ replicate num_ctx (I.Prim int64, mempty)+ ++ zip rettype' (map (shiftRetAls num_ctx) retals) ) - let all_params = shapeparams ++ concat params'- attrs' <- internaliseAttrs attrs let fd =@@ -73,7 +75,7 @@ attrs' (internaliseFunName fname) rettype'- all_params+ (foldMap toList all_params) body' if null params'@@ -83,9 +85,10 @@ fname fd ( shapenames,- map declTypeOf $ concat params',- all_params,- applyRetType rettype' all_params+ map declTypeOf $ foldMap (foldMap toList) params',+ foldMap toList all_params,+ fmap (`zip` map snd rettype')+ . applyRetType (map fst rettype') (foldMap toList all_params) ) case entry of@@ -98,14 +101,16 @@ generateEntryPoint types (E.EntryPoint e_params e_rettype) vb = do let (E.ValBind _ ofname _ (Info rettype) tparams params _ _ attrs loc) = vb bindingFParams tparams params $ \shapeparams params' -> do- let entry_rettype = internaliseEntryReturnType rettype+ let all_params = map pure shapeparams ++ concat params'+ (entry_rettype, retals) =+ unzip $ map unzip $ internaliseEntryReturnType (map (fmap paramDeclType) all_params) rettype (entry', opaques) = entryPoint types (baseName ofname)- (zip e_params params')+ (zip e_params $ map (foldMap toList) params') (e_rettype, map (map I.rankShaped) entry_rettype)- args = map (I.Var . I.paramName) $ concat params'+ args = map (I.Var . I.paramName) $ foldMap (foldMap toList) params' addOpaques opaques @@ -117,20 +122,25 @@ Just ses -> pure ses Nothing ->- fst <$> funcall "entry_result" (E.qualName ofname) args loc+ funcall "entry_result" (E.qualName ofname) args loc ctx <- extractShapeContext (zeroExts $ concat entry_rettype) <$> mapM (fmap I.arrayDims . subExpType) vals- pure (subExpsRes $ ctx ++ vals, map (const (I.Prim int64)) ctx)+ pure (subExpsRes $ ctx ++ vals, map (const (I.Prim int64, mempty)) ctx) attrs' <- internaliseAttrs attrs+ let num_ctx = length ctx_ts addFunDef $ I.FunDef (Just entry') attrs' ("entry_" <> baseName ofname)- (ctx_ts ++ zeroExts (concat entry_rettype))- (shapeparams ++ concat params')+ ( ctx_ts+ ++ zip+ (zeroExts (concat entry_rettype))+ (map (shiftRetAls num_ctx) $ concat retals)+ )+ (shapeparams ++ foldMap (foldMap toList) params') entry_body where zeroExts ts = generaliseExtTypes ts ts@@ -320,19 +330,6 @@ se <- letSubExp desc (I.BasicOp $ I.Iota num_elems start' step it) pure [se]-internaliseAppExp desc (E.AppRes et ext) (E.Coerce e dt loc) = do- ses <- internaliseExp desc e- ts <- internaliseReturnType (E.RetType ext (E.toStruct et)) <$> mapM subExpType ses- dt' <- typeExpForError dt- forM (zip ses ts) $ \(e', t') -> do- dims <- arrayDims <$> subExpType e'- let parts =- ["Value of (core language) shape ("]- ++ intersperse ", " (map (ErrorVal int64) dims)- ++ [") cannot match shape of type `"]- ++ dt'- ++ ["`."]- ensureExtShape (errorMsg parts) loc (I.fromDecl t') desc e' internaliseAppExp desc (E.AppRes et ext) e@E.Apply {} = case findFuncall e of (FunctionHole loc, _args) -> do@@ -343,7 +340,7 @@ -- application. One caveat is that we need to replace any -- existential sizes, too (with zeroes, because they don't -- matter).- let subst = zip ext $ repeat $ E.SizeSubst $ E.ConstSize 0+ let subst = zip ext $ repeat $ E.ExpSubst $ E.sizeFromInteger 0 mempty et' = E.applySubst (`lookup` subst) et internaliseExp desc (E.Hole (Info et') loc) (FunctionName qfname, args) -> do@@ -386,10 +383,10 @@ let tag ses = [(se, I.Observe) | se <- ses] args' <- reverse <$> mapM (internaliseArg arg_desc) (reverse args) let args'' = concatMap tag args'- letValExp' desc $ I.Apply fname args'' [I.Prim rettype] (Safe, loc, [])+ letValExp' desc $ I.Apply fname args'' [(I.Prim rettype, mempty)] (Safe, loc, []) | otherwise -> do args' <- concat . reverse <$> mapM (internaliseArg arg_desc) (reverse args)- fst <$> funcall desc qfname args' loc+ funcall desc qfname args' loc internaliseAppExp desc _ (E.LetPat sizes pat e body _) = internalisePat desc sizes pat e $ internaliseExp desc body internaliseAppExp _ _ (E.LetFun ofname _ _ _) =@@ -468,7 +465,7 @@ ts <- mapM subExpType mergeinit bindingLoopParams sparams' mergepat ts $ \shapepat mergepat' ->- bindingLambdaParams [x] (map rowType arr_ts) $ \x_params -> do+ bindingLambdaParams [toParam E.Observe <$> x] (map rowType arr_ts) $ \x_params -> do let loopvars = zip x_params arr' forLoop mergepat' shapepat mergeinit $ I.ForLoop i Int64 w loopvars@@ -508,8 +505,15 @@ | not $ primType $ paramType p -> Reshape I.ReshapeCoerce (I.arrayShape $ paramType p) v _ -> SubExp se- internaliseExp1 "loop_cond" cond + -- As the condition expression is inserted twice, we have to+ -- avoid shadowing (#1935).+ (cond_stms, cond') <-+ uncurry (flip renameStmsWith)+ =<< collectStms (internaliseExp1 "loop_cond" cond)+ addStms cond_stms+ pure cond'+ addStms init_loop_cond_stms bodyFromStms $ do@@ -547,7 +551,7 @@ ) internaliseAppExp desc _ (E.LetWith name src idxs ve body loc) = do let pat = E.Id (E.identName name) (E.identType name) loc- src_t = E.fromStruct <$> E.identType src+ src_t = E.identType src e = E.Update (E.Var (E.qualName $ E.identName src) src_t loc) idxs ve loc internaliseExp desc $ E.AppExp@@ -581,7 +585,7 @@ internaliseExp desc e internaliseExp desc (E.Hole (Info t) loc) = do let msg = docText $ "Reached hole of type: " <> align (pretty t)- ts = internaliseType (E.toStruct t)+ ts = foldMap toList $ internaliseType (E.toStruct t) c <- assert "hole_c" (constant False) (errorMsg [ErrorString msg]) loc case mapM hasStaticShape ts of Nothing ->@@ -644,7 +648,7 @@ letSubExp desc $ I.BasicOp $ I.Reshape I.ReshapeArbitrary new_shape' flat_arr | otherwise = do es' <- mapM (internaliseExp "arr_elem") es- let arr_t_ext = internaliseType $ E.toStruct arr_t+ let arr_t_ext = foldMap toList $ internaliseType $ E.toStruct arr_t rowtypes <- case mapM (fmap rowType . hasStaticShape . I.fromDecl) arr_t_ext of@@ -685,6 +689,19 @@ Just ([], [e]) internaliseExp desc (E.Ascript e _ _) = internaliseExp desc e+internaliseExp desc (E.Coerce e _ (Info et) loc) = do+ ses <- internaliseExp desc e+ ts <- internaliseCoerceType (E.toStruct et) <$> mapM subExpType ses+ dt' <- typeExpForError $ toStruct et+ forM (zip ses ts) $ \(e', t') -> do+ dims <- arrayDims <$> subExpType e'+ let parts =+ ["Value of (core language) shape ("]+ ++ intersperse ", " (map (ErrorVal int64) dims)+ ++ [") cannot match shape of type `"]+ ++ dt'+ ++ ["`."]+ ensureExtShape (errorMsg parts) loc (I.fromDecl t') desc e' internaliseExp desc (E.Negate e _) = do e' <- internaliseExp1 "negate_arg" e et <- subExpType e'@@ -728,7 +745,7 @@ internaliseExp desc (E.RecordUpdate src fields ve _ _) = do src' <- internaliseExp desc src ve' <- internaliseExp desc ve- replace (E.typeOf src `setAliases` ()) fields ve' src'+ replace (E.typeOf src) fields ve' src' where replace (E.Scalar (E.Record m)) (f : fs) ve' src' | Just t <- M.lookup f m = do@@ -817,12 +834,11 @@ -- overloaded. internaliseExp desc (E.Project k e (Info rt) _) = do let i' = sum . map internalisedTypeSize $- case E.typeOf e `setAliases` () of+ case E.typeOf e of E.Scalar (Record fs) -> map snd $ takeWhile ((/= k) . fst) $ sortFields fs t -> [t]- take (internalisedTypeSize $ rt `setAliases` ()) . drop i'- <$> internaliseExp desc e+ take (internalisedTypeSize rt) . drop i' <$> internaliseExp desc e internaliseExp _ e@E.Lambda {} = error $ "internaliseExp: Unexpected lambda at " ++ locStr (srclocOf e) internaliseExp _ e@E.OpSection {} =@@ -849,7 +865,7 @@ _ -> pure () pure arg' -internalisePatLit :: E.PatLit -> E.PatType -> I.PrimValue+internalisePatLit :: E.PatLit -> E.StructType -> I.PrimValue internalisePatLit (E.PatLitPrim v) _ = internalisePrimValue v internalisePatLit (E.PatLitInt x) (E.Scalar (E.Prim (E.Signed it))) =@@ -862,7 +878,7 @@ error $ "Nonsensical pattern and type: " ++ show (l, t) generateCond ::- E.Pat ->+ E.Pat StructType -> [I.SubExp] -> InternaliseM ([Maybe I.PrimValue], [I.SubExp]) generateCond orig_p orig_ses = do@@ -927,7 +943,7 @@ internalisePat :: String -> [E.SizeBinder VName] ->- E.Pat ->+ E.Pat StructType -> E.Exp -> InternaliseM a -> InternaliseM a@@ -935,19 +951,19 @@ ses <- internaliseExp desc' e internalisePat' sizes p ses m where- desc' = case S.toList $ E.patIdents p of+ desc' = case E.patIdents p of [v] -> baseString $ E.identName v _ -> desc internalisePat' :: [E.SizeBinder VName] ->- E.Pat ->+ E.Pat StructType -> [I.SubExp] -> InternaliseM a -> InternaliseM a internalisePat' sizes p ses m = do ses_ts <- mapM subExpType ses- stmPat p ses_ts $ \pat_names -> do+ stmPat (toParam E.Observe <$> p) ses_ts $ \pat_names -> do bindExtSizes (AppRes (E.patternType p) (map E.sizeName sizes)) ses forM_ (zip pat_names ses) $ \(v, se) -> letBindNames [v] $ I.BasicOp $ I.SubExp se@@ -1031,7 +1047,7 @@ ( eBinOp (Add Int64 I.OverflowWrap) x- (eBinOp (Sub Int64 I.OverflowWrap) y (eSignum $ toExp s'))+ (eBinOp (Sub Int64 I.OverflowWrap) y (eSignum y)) ) y n <- letSubExp "n" =<< divRounding (toExp j_m_i) (toExp s')@@ -1065,14 +1081,14 @@ i_lte_j <- letSubExp "i_lte_j" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) i' j' forwards_ok <- letSubExp "forwards_ok"- =<< eAll [zero_lte_i, zero_lte_i, i_lte_j, zero_leq_i_p_m_t_s, i_p_m_t_s_lth_w]+ =<< eAll [zero_lte_i, i_lte_j, zero_leq_i_p_m_t_s, i_p_m_t_s_lth_w] negone_lte_j <- letSubExp "negone_lte_j" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) negone j' j_lte_i <- letSubExp "j_lte_i" $ I.BasicOp $ I.CmpOp (I.CmpSle Int64) j' i' backwards_ok <- letSubExp "backwards_ok" =<< eAll- [negone_lte_j, negone_lte_j, j_lte_i, zero_leq_i_p_m_t_s, i_p_m_t_s_leq_w]+ [negone_lte_j, j_lte_i, zero_leq_i_p_m_t_s, i_p_m_t_s_leq_w] slice_ok <- letSubExp "slice_ok"@@ -1471,7 +1487,7 @@ internaliseLambda :: InternaliseLambda internaliseLambda (E.Parens e _) rowtypes = internaliseLambda e rowtypes-internaliseLambda (E.Lambda params body _ (Info (_, RetType _ rettype)) _) rowtypes =+internaliseLambda (E.Lambda params body _ (Info (RetType _ rettype)) _) rowtypes = bindingLambdaParams params rowtypes $ \params' -> do body' <- internaliseBody "lam" body rettype' <- internaliseLambdaReturnType rettype =<< bodyExtType body'@@ -1677,15 +1693,15 @@ arrs <- internaliseExpToVars "unflatten_arr" arr n' <- internaliseExp1 "n" n m' <- internaliseExp1 "m" m- -- The unflattened dimension needs to have the same number of elements- -- as the original dimension.+ -- Each dimension must be nonnegative, and the unflattened+ -- dimension needs to have the same number of elements as the+ -- original dimension. old_dim <- I.arraysSize 0 <$> mapM lookupType arrs dim_ok <-- letSubExp "dim_ok"- =<< eCmpOp- (I.CmpEq I.int64)- (eBinOp (I.Mul Int64 I.OverflowUndef) (eSubExp n') (eSubExp m'))- (eSubExp old_dim)+ letSubExp "dim_ok" <=< toExp $+ pe64 old_dim .==. pe64 n' * pe64 m'+ .&&. pe64 n' .>=. 0+ .&&. pe64 m' .>=. 0 dim_ok_cert <- assert "dim_ok_cert"@@ -1736,24 +1752,19 @@ let conc xarr yarr = I.BasicOp $ I.Concat 0 (xarr :| [yarr]) ressize mapM (letSubExp desc) $ zipWith conc xs ys- handleRest [offset, e] "rotate" = Just $ \desc -> do- offset' <- internaliseExp1 "rotation_offset" offset- internaliseOperation desc e $ \v -> do- r <- I.arrayRank <$> lookupType v- let zero = intConst Int64 0- offsets = offset' : replicate (r - 1) zero- pure $ I.Rotate offsets v handleRest [e] "transpose" = Just $ \desc -> internaliseOperation desc e $ \v -> do r <- I.arrayRank <$> lookupType v pure $ I.Rearrange ([1, 0] ++ [2 .. r - 1]) v handleRest [x, y] "zip" = Just $ \desc ->- mapM (letSubExp "zip_copy" . BasicOp . Copy)+ mapM (letSubExp "zip_copy" . BasicOp . Replicate mempty . I.Var) =<< ( (++) <$> internaliseExpToVars (desc ++ "_zip_x") x <*> internaliseExpToVars (desc ++ "_zip_y") y )- handleRest [x] "unzip" = Just $ flip internaliseExp x+ handleRest [x] "unzip" = Just $ \desc ->+ mapM (letSubExp desc . BasicOp . Replicate mempty . I.Var)+ =<< internaliseExpToVars desc x handleRest [arr, offset, n1, s1, n2, s2] "flat_index_2d" = Just $ \desc -> do flatIndexHelper desc loc arr offset [(n1, s1), (n2, s2)] handleRest [arr1, offset, s1, s2, arr2] "flat_update_2d" = Just $ \desc -> do@@ -1840,7 +1851,7 @@ bodyNames = indexName <> valueNames bodyParams = zipWith (I.Param mempty) bodyNames paramTypes - -- This body is prettyString boring right now, as every input is exactly the output.+ -- This body is boring right now, as every input is exactly the output. -- But it can get funky later on if fused with something else. body <- localScope (scopeOfLParams bodyParams) . buildBody_ $ do let outs = concat (replicate (length valueNames) indexName) ++ valueNames@@ -1962,10 +1973,9 @@ QualName VName -> [SubExp] -> SrcLoc ->- InternaliseM ([SubExp], [I.ExtType])+ InternaliseM [SubExp] funcall desc (QualName _ fname) args loc = do- (shapes, value_paramts, fun_params, rettype_fun) <-- lookupFunction fname+ (shapes, value_paramts, fun_params, rettype_fun) <- lookupFunction fname argts <- mapM subExpType args shapeargs <- argShapes shapes fun_params argts@@ -2000,10 +2010,8 @@ Just ts -> do safety <- askSafety attrs <- asks envAttrs- ses <-- attributing attrs . letValExp' desc $- I.Apply (internaliseFunName fname) (zip args' diets) ts (safety, loc, mempty)- pure (ses, map I.fromDecl ts)+ attributing attrs . letValExp' desc $+ I.Apply (internaliseFunName fname) (zip args' diets) ts (safety, loc, mempty) -- Bind existential names defined by an expression, based on the -- concrete values that expression evaluated to. This most@@ -2012,7 +2020,7 @@ -- language. bindExtSizes :: AppRes -> [SubExp] -> InternaliseM () bindExtSizes (AppRes ret retext) ses = do- let ts = internaliseType $ E.toStruct ret+ let ts = foldMap toList $ internaliseType $ E.toStruct ret ses_ts <- mapM subExpType ses let combine t1 t2 =@@ -2148,53 +2156,41 @@ (resultBodyM [this_one]) (resultBodyM [next_one]) -typeExpForError :: E.TypeExp Info VName -> InternaliseM [ErrorMsgPart SubExp]-typeExpForError (E.TEVar qn _) =- pure [ErrorString $ prettyText qn]-typeExpForError (E.TEParens te _) = do- msg <- typeExpForError te- pure $ ["("] <> msg <> [")"]-typeExpForError (E.TEUnique te _) =- ("*" :) <$> typeExpForError te-typeExpForError (E.TEDim dims te _) =- (ErrorString ("?" <> dims' <> ".") :) <$> typeExpForError te+sizeExpForError :: E.Size -> InternaliseM [ErrorMsgPart SubExp]+sizeExpForError e = do+ e' <- internaliseExp1 "size" e+ pure ["[", ErrorVal int64 e', "]"]++typeExpForError :: E.TypeBase Size u -> InternaliseM [ErrorMsgPart SubExp]+typeExpForError (E.Scalar (E.Prim t)) = pure [ErrorString $ prettyText t]+typeExpForError (E.Scalar (E.TypeVar _ v args)) = do+ args' <- concat <$> mapM onArg args+ pure $ intersperse " " $ ErrorString (prettyText v) : args' where- dims' = mconcat (map onDim dims)- onDim d = "[" <> prettyText d <> "]"-typeExpForError (E.TEArray d te _) =- (<>) <$> sizeExpForError d <*> typeExpForError te-typeExpForError (E.TETuple tes _) = do- tes' <- mapM typeExpForError tes- pure $ ["("] ++ intercalate [", "] tes' ++ [")"]-typeExpForError (E.TERecord fields _) = do- fields' <- mapM onField fields- pure $ ["{"] ++ intercalate [", "] fields' ++ ["}"]+ onArg (TypeArgDim d) = sizeExpForError d+ onArg (TypeArgType t) = typeExpForError t+typeExpForError (E.Scalar (E.Record fs))+ | Just ts <- E.areTupleFields fs = do+ ts' <- mapM typeExpForError ts+ pure $ ["("] ++ intercalate [", "] ts' ++ [")"]+ | otherwise = do+ fs' <- mapM onField $ M.toList fs+ pure $ ["{"] ++ intercalate [", "] fs' ++ ["}"] where onField (k, te) = (ErrorString (prettyText k <> ": ") :) <$> typeExpForError te-typeExpForError (E.TEArrow _ t1 t2 _) = do- t1' <- typeExpForError t1- t2' <- typeExpForError t2- pure $ t1' ++ [" -> "] ++ t2'-typeExpForError (E.TEApply t arg _) = do- t' <- typeExpForError t- arg' <- case arg of- TypeArgExpType argt -> typeExpForError argt- TypeArgExpSize d -> sizeExpForError d- pure $ t' ++ [" "] ++ arg'-typeExpForError (E.TESum cs _) = do- cs' <- mapM (onClause . snd) cs+typeExpForError (E.Array _ shape et) = do+ shape' <- mconcat <$> mapM sizeExpForError (E.shapeDims shape)+ et' <- typeExpForError $ Scalar et+ pure $ shape' ++ et'+typeExpForError (E.Scalar (E.Sum cs)) = do+ cs' <- mapM onConstructor $ M.toList cs pure $ intercalate [" | "] cs' where- onClause c = do- c' <- mapM typeExpForError c- pure $ intercalate [" "] c'--sizeExpForError :: E.SizeExp Info VName -> InternaliseM [ErrorMsgPart SubExp]-sizeExpForError (SizeExp e _) = do- e' <- internaliseExp1 "size" e- pure ["[", ErrorVal int64 e', "]"]-sizeExpForError SizeExpAny {} = pure ["[]"]+ onConstructor (c, ts) = do+ ts' <- mapM typeExpForError ts+ pure $ ErrorString ("#" <> prettyText c <> " ") : intercalate [" "] ts'+typeExpForError (E.Scalar Arrow {}) = pure ["#<fun>"] -- A smart constructor that compacts neighbouring literals for easier -- reading in the IR.
+ src/Futhark/Internalise/FullNormalise.hs view
@@ -0,0 +1,367 @@+-- | This full normalisation module converts a well-typed, polymorphic,+-- module-free Futhark program into an equivalent with only simple expresssions.+-- Notably, all non-trivial expression are converted into a list of+-- let-bindings to make them simpler, with no nested apply, nested lets...+-- This module only performs synthatic operations.+--+-- Also, it performs desugaring that is:+-- * Turn operator section into lambda+-- * turn BinOp into application (&& and || are converted to if structure)+-- * turn `let x [i] = e1` into `let x = x with [i] = e1`+-- * binds all implicit sizes+--+-- This is currently not done for expressions inside sizes, this processing+-- still needed in monomorphisation for now.+module Futhark.Internalise.FullNormalise (transformProg) where++import Control.Monad.Reader+import Control.Monad.State+import Data.Bifunctor+import Data.List.NonEmpty qualified as NE+import Data.Map qualified as M+import Data.Text qualified as T+import Futhark.MonadFreshNames+import Language.Futhark+import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Types++-- Modifier to apply on binding, this is used to propagate attributes and move assertions+data BindModifier+ = Ass Exp (Info T.Text) SrcLoc+ | Att (AttrInfo VName)++-- Apply a list of modifiers, removing the assertions as it is not needed to check them multiple times+applyModifiers :: Exp -> [BindModifier] -> (Exp, [BindModifier])+applyModifiers =+ foldr f . (,[])+ where+ f (Ass ass txt loc) (body, modifs) =+ (Assert ass body txt loc, modifs)+ f (Att attr) (body, modifs) =+ (Attr attr body mempty, Att attr : modifs)++-- A binding that occurs in the calculation flow+data Binding+ = PatBind [SizeBinder VName] (Pat StructType) Exp+ | FunBind VName ([TypeParam], [Pat ParamType], Maybe (TypeExp Info VName), Info ResRetType, Exp)++type NormState = (([Binding], [BindModifier]), VNameSource)++-- | Main monad of this module, the state as 3 parts:+-- * the VNameSource to produce new names+-- * the [Binding] is the accumulator for the result+-- It behave a bit like a writer+-- * the [BindModifier] is the current list of modifiers to apply to the introduced bindings+-- It behave like a reader for attributes modifier, and as a state for assertion,+-- they have to be in the same list to conserve their order+-- Direct interaction with the inside state should be done with caution, that's why their+-- no instance of `MonadState`.+newtype OrderingM a = OrderingM (StateT NormState (Reader String) a)+ deriving+ (Functor, Applicative, Monad, MonadReader String, MonadState NormState)++instance MonadFreshNames OrderingM where+ getNameSource = OrderingM $ gets snd+ putNameSource = OrderingM . modify . second . const++addModifier :: BindModifier -> OrderingM ()+addModifier = OrderingM . modify . first . second . (:)++rmModifier :: OrderingM ()+rmModifier = OrderingM $ modify $ first $ second tail++addBind :: Binding -> OrderingM ()+addBind (PatBind s p e) = do+ modifs <- gets $ snd . fst+ let (e', modifs') = applyModifiers e modifs+ modify $ first $ bimap (PatBind (s <> implicit) p e' :) (const modifs')+ where+ implicit = case e of+ (AppExp _ (Info (AppRes _ ext))) -> map (`SizeBinder` mempty) ext+ _ -> []+addBind b@FunBind {} =+ OrderingM $ modify $ first $ first (b :)++runOrdering :: MonadFreshNames m => OrderingM a -> m (a, [Binding])+runOrdering (OrderingM m) =+ modifyNameSource $ mod_tup . flip runReader "tmp" . runStateT m . (([], []),)+ where+ mod_tup (a, ((binds, modifs), src)) =+ if null modifs+ then ((a, binds), src)+ else error "not all bind modifiers were freed"++naming :: String -> OrderingM a -> OrderingM a+naming s = local (const s)++-- | From now, we say an expression is "final" if it's going to be stored in a let-bind+-- or is at the end of the body e.g. after all lets++-- Replace a non-final expression by a let-binded variable+nameExp :: Bool -> Exp -> OrderingM Exp+nameExp True e = pure e+nameExp False e = do+ name <- newNameFromString =<< ask -- "e<{" ++ prettyString e ++ "}>"+ let ty = typeOf e+ loc = srclocOf e+ pat = Id name (Info ty) loc+ addBind $ PatBind [] pat e+ pure $ Var (qualName name) (Info ty) loc++-- An evocative name to use when naming subexpressions of the+-- expression bound to this pattern.+patRepName :: Pat t -> String+patRepName (PatAscription p _ _) = patRepName p+patRepName (Id v _ _) = baseString v+patRepName _ = "tmp"++expRepName :: Exp -> String+expRepName (Var v _ _) = prettyString v+expRepName e = "d<{" ++ prettyString (bareExp e) ++ "}>"++-- An evocative name to use when naming arguments to an application.+argRepName :: Exp -> Int -> String+argRepName e i = expRepName e <> "_arg" <> show i++-- Modify an expression as describe in module introduction,+-- introducing the let-bindings in the state.+getOrdering :: Bool -> Exp -> OrderingM Exp+getOrdering final (Assert ass e txt loc) = do+ ass' <- getOrdering False ass+ l_prev <- OrderingM $ gets $ length . snd . fst+ addModifier $ Ass ass' txt loc+ e' <- getOrdering final e+ l_after <- OrderingM $ gets $ length . snd . fst+ -- if the list of modifier has reduced in size, that means that+ -- all assertions as been inserted,+ -- else, we have to introduce the assertion ourself+ if l_after <= l_prev+ then pure e'+ else do+ rmModifier+ pure $ Assert ass' e' txt loc+getOrdering final (Attr attr e loc) = do+ -- propagate attribute+ addModifier $ Att attr+ e' <- getOrdering final e+ rmModifier+ pure $ Attr attr e' loc+getOrdering _ e@Literal {} = pure e+getOrdering _ e@IntLit {} = pure e+getOrdering _ e@FloatLit {} = pure e+getOrdering _ e@StringLit {} = pure e+getOrdering _ e@Hole {} = pure e -- can we still have some ?+getOrdering _ e@Var {} = pure e+getOrdering final (Parens e _) = getOrdering final e+getOrdering final (QualParens _ e _) = getOrdering final e+getOrdering _ (TupLit es loc) = do+ es' <- mapM (getOrdering False) es+ pure $ TupLit es' loc+getOrdering _ (RecordLit fs loc) = do+ fs' <- mapM f fs+ pure $ RecordLit fs' loc+ where+ f (RecordFieldExplicit n e floc) = do+ e' <- getOrdering False e+ pure $ RecordFieldExplicit n e' floc+ f field@RecordFieldImplicit {} = pure field+getOrdering _ (ArrayLit es ty loc) = do+ es' <- mapM (getOrdering False) es+ pure $ ArrayLit es' ty loc+getOrdering _ (Project n e ty loc) = do+ e' <- getOrdering False e+ pure $ Project n e' ty loc+getOrdering _ (Negate e loc) = do+ e' <- getOrdering False e+ pure $ Negate e' loc+getOrdering _ (Not e loc) = do+ e' <- getOrdering False e+ pure $ Not e' loc+getOrdering final (Constr n es ty loc) = do+ es' <- mapM (getOrdering False) es+ nameExp final $ Constr n es' ty loc+getOrdering final (Update eb slice eu loc) = do+ eu' <- getOrdering False eu+ slice' <- astMap mapper slice+ eb' <- getOrdering False eb+ nameExp final $ Update eb' slice' eu' loc+ where+ mapper = identityMapper {mapOnExp = getOrdering False}+getOrdering final (RecordUpdate eb ns eu ty loc) = do+ eb' <- getOrdering False eb+ eu' <- getOrdering False eu+ nameExp final $ RecordUpdate eb' ns eu' ty loc+getOrdering final (Lambda params body mte ret loc) = do+ body' <- transformBody body+ nameExp final $ Lambda params body' mte ret loc+getOrdering _ (OpSection qn ty loc) =+ pure $ Var qn ty loc+getOrdering final (OpSectionLeft op ty e (Info (xp, _, xext), Info (yp, yty)) (Info (RetType dims ret), Info exts) loc) = do+ x <- getOrdering False e+ yn <- newNameFromString "y"+ let y = Var (qualName yn) (Info $ toStruct yty) mempty+ ret' = applySubst (pSubst x y) ret+ body =+ mkApply (Var op ty mempty) [(Observe, xext, x), (Observe, Nothing, y)] $+ AppRes (toStruct ret') exts+ nameExp final $ Lambda [Id yn (Info yty) mempty] body Nothing (Info (RetType dims ret')) loc+ where+ pSubst x y vn+ | Named p <- xp, p == vn = Just $ ExpSubst x+ | Named p <- yp, p == vn = Just $ ExpSubst y+ | otherwise = Nothing+getOrdering final (OpSectionRight op ty e (Info (xp, xty), Info (yp, _, yext)) (Info (RetType dims ret)) loc) = do+ xn <- newNameFromString "x"+ y <- getOrdering False e+ let x = Var (qualName xn) (Info $ toStruct xty) mempty+ ret' = applySubst (pSubst x y) ret+ body = mkApply (Var op ty mempty) [(Observe, Nothing, x), (Observe, yext, y)] $ AppRes (toStruct ret') []+ nameExp final $ Lambda [Id xn (Info xty) mempty] body Nothing (Info (RetType dims ret')) loc+ where+ pSubst x y vn+ | Named p <- xp, p == vn = Just $ ExpSubst x+ | Named p <- yp, p == vn = Just $ ExpSubst y+ | otherwise = Nothing+getOrdering final (ProjectSection names (Info ty) loc) = do+ xn <- newNameFromString "x"+ let (xty, RetType dims ret) = case ty of+ Scalar (Arrow _ _ d xty' ret') -> (toParam d xty', ret')+ _ -> error $ "not a function type for project section: " ++ prettyString ty+ x = Var (qualName xn) (Info $ toStruct xty) mempty+ body = foldl project x names+ nameExp final $ Lambda [Id xn (Info xty) mempty] body Nothing (Info (RetType dims ret)) loc+ where+ project e field =+ case typeOf e of+ Scalar (Record fs)+ | Just t <- M.lookup field fs ->+ Project field e (Info t) mempty+ t ->+ error $+ "desugar ProjectSection: type "+ ++ prettyString t+ ++ " does not have field "+ ++ prettyString field+getOrdering final (IndexSection slice (Info ty) loc) = do+ slice' <- astMap mapper slice+ xn <- newNameFromString "x"+ let (xty, RetType dims ret) = case ty of+ Scalar (Arrow _ _ d xty' ret') -> (toParam d xty', ret')+ _ -> error $ "not a function type for index section: " ++ prettyString ty+ x = Var (qualName xn) (Info $ toStruct xty) mempty+ body = AppExp (Index x slice' loc) (Info (AppRes (toStruct ret) []))+ nameExp final $ Lambda [Id xn (Info xty) mempty] body Nothing (Info (RetType dims ret)) loc+ where+ mapper = identityMapper {mapOnExp = getOrdering False}+getOrdering _ (Ascript e _ _) = getOrdering False e+getOrdering final (AppExp (Apply f args loc) resT) = do+ args' <-+ NE.reverse <$> mapM onArg (NE.reverse (NE.zip args (NE.fromList [0 ..])))+ f' <- getOrdering False f+ nameExp final $ AppExp (Apply f' args' loc) resT+ where+ onArg ((d, e), i) =+ naming (argRepName f i) $ (d,) <$> getOrdering False e+getOrdering final (Coerce e te t loc) = do+ e' <- getOrdering False e+ nameExp final $ Coerce e' te t loc+getOrdering final (AppExp (Range start stride end loc) resT) = do+ start' <- getOrdering False start+ stride' <- mapM (getOrdering False) stride+ end' <- mapM (getOrdering False) end+ nameExp final $ AppExp (Range start' stride' end' loc) resT+getOrdering final (AppExp (LetPat sizes pat expr body _) _) = do+ expr' <- naming (patRepName pat) $ getOrdering True expr+ addBind $ PatBind sizes pat expr'+ getOrdering final body+getOrdering final (AppExp (LetFun vn (tparams, params, mrettype, rettype, body) e _) _) = do+ body' <- transformBody body+ addBind $ FunBind vn (tparams, params, mrettype, rettype, body')+ getOrdering final e+getOrdering final (AppExp (If cond et ef loc) resT) = do+ cond' <- getOrdering True cond+ et' <- transformBody et+ ef' <- transformBody ef+ nameExp final $ AppExp (If cond' et' ef' loc) resT+getOrdering final (AppExp (DoLoop sizes pat einit form body loc) resT) = do+ einit' <- getOrdering False einit+ form' <- case form of+ For ident e -> For ident <$> getOrdering True e+ ForIn fpat e -> ForIn fpat <$> getOrdering True e+ While e -> While <$> transformBody e+ body' <- transformBody body+ nameExp final $ AppExp (DoLoop sizes pat einit' form' body' loc) resT+getOrdering final (AppExp (BinOp (op, oloc) opT (el, Info elp) (er, Info erp) loc) (Info resT)) = do+ expr' <- case (isOr, isAnd) of+ (True, _) -> do+ el' <- naming "or_lhs" $ getOrdering True el+ er' <- naming "or_rhs" $ transformBody er+ pure $ AppExp (If el' (Literal (BoolValue True) mempty) er' loc) (Info resT)+ (_, True) -> do+ el' <- naming "and_lhs" $ getOrdering True el+ er' <- naming "and_rhs" $ transformBody er+ pure $ AppExp (If el' er' (Literal (BoolValue False) mempty) loc) (Info resT)+ (False, False) -> do+ el' <- naming (prettyString op <> "_lhs") $ getOrdering False el+ er' <- naming (prettyString op <> "_rhs") $ getOrdering False er+ pure $ mkApply (Var op opT oloc) [(Observe, elp, el'), (Observe, erp, er')] resT+ nameExp final expr'+ where+ isOr = baseName (qualLeaf op) == "||"+ isAnd = baseName (qualLeaf op) == "&&"+getOrdering final (AppExp (LetWith (Ident dest dty dloc) (Ident src sty sloc) slice e body loc) _) = do+ e' <- getOrdering False e+ slice' <- astMap mapper slice+ addBind $ PatBind [] (Id dest dty dloc) (Update (Var (qualName src) sty sloc) slice' e' loc)+ getOrdering final body+ where+ mapper = identityMapper {mapOnExp = getOrdering False}+getOrdering final (AppExp (Index e slice loc) resT) = do+ e' <- getOrdering False e+ slice' <- astMap mapper slice+ nameExp final $ AppExp (Index e' slice' loc) resT+ where+ mapper = identityMapper {mapOnExp = getOrdering False}+getOrdering final (AppExp (Match expr cs loc) resT) = do+ expr' <- getOrdering False expr+ cs' <- mapM f cs+ nameExp final $ AppExp (Match expr' cs' loc) resT+ where+ f (CasePat pat body cloc) = do+ body' <- transformBody body+ pure (CasePat pat body' cloc)++-- Transform a body, e.g. the expression of a valbind,+-- branches of an if/match...+-- Note that this is not producing an OrderingM, produce+-- a complete separtion of states.+transformBody :: MonadFreshNames m => Exp -> m Exp+transformBody e = do+ (e', pre_eval) <- runOrdering (getOrdering True e)+ pure $ foldl f e' pre_eval+ where+ appRes = case e of+ (AppExp _ r) -> r+ _ -> Info $ AppRes (typeOf e) []++ f body (PatBind sizes p expr) =+ AppExp+ ( LetPat+ sizes+ p+ expr+ body+ mempty+ )+ appRes+ f body (FunBind vn infos) =+ AppExp (LetFun vn infos body mempty) appRes++transformDec :: MonadFreshNames m => Dec -> m Dec+transformDec (ValDec valbind) = do+ body' <- transformBody $ valBindBody valbind+ pure $ ValDec (valbind {valBindBody = body'})+transformDec d = pure d++transformProg :: MonadFreshNames m => [Dec] -> m [Dec]+transformProg = mapM transformDec
src/Futhark/Internalise/LiftLambdas.hs view
@@ -13,13 +13,17 @@ import Data.Set qualified as S import Futhark.IR.Pretty () import Futhark.MonadFreshNames+import Futhark.Util (nubOrd) import Language.Futhark import Language.Futhark.Traversals -newtype Env = Env {envReplace :: M.Map VName Exp}+data Env = Env+ { envReplace :: M.Map VName Exp,+ envVtable :: M.Map VName StructType+ } initialEnv :: Env-initialEnv = Env mempty+initialEnv = Env mempty mempty data LiftState = State { stateNameSource :: VNameSource,@@ -53,61 +57,64 @@ replacing v e = local $ \env -> env {envReplace = M.insert v e $ envReplace env} -existentials :: Exp -> S.Set VName-existentials e =- let onArg (Info (_, pdim), _) =- maybeToList pdim- here = case e of- AppExp (Apply _ args _) (Info res) ->- S.fromList (foldMap onArg args <> appResExt res)- AppExp _ (Info res) ->- S.fromList (appResExt res)- _ ->- mempty+bindingParams :: [VName] -> [Pat ParamType] -> LiftM a -> LiftM a+bindingParams sizes params = local $ \env ->+ env+ { envVtable =+ M.fromList (map (second toStruct) (foldMap patternMap params) <> map (,i64) sizes)+ <> envVtable env+ }+ where+ i64 = Scalar $ Prim $ Signed Int64 - m = identityMapper {mapOnExp = \e' -> modify (<> existentials e') >> pure e'}- in execState (astMap m e) here+bindingLetPat :: [VName] -> Pat StructType -> LiftM a -> LiftM a+bindingLetPat sizes pat = local $ \env ->+ env+ { envVtable =+ M.fromList (map (second toStruct) (patternMap pat) <> map (,i64) sizes)+ <> envVtable env+ }+ where+ i64 = Scalar $ Prim $ Signed Int64 -freeSizes :: S.Set VName -> FV-freeSizes vs =- FV $ M.fromList $ zip (S.toList vs) $ repeat $ Scalar $ Prim $ Signed Int64+bindingForm :: LoopFormBase Info VName -> LiftM a -> LiftM a+bindingForm (For i _) = bindingLetPat [] (Id (identName i) (identType i) mempty)+bindingForm (ForIn p _) = bindingLetPat [] p+bindingForm While {} = id -liftFunction :: VName -> [TypeParam] -> [Pat] -> StructRetType -> Exp -> LiftM Exp+toRet :: TypeBase Size u -> TypeBase Size Uniqueness+toRet = second (const Nonunique)++liftFunction :: VName -> [TypeParam] -> [Pat ParamType] -> ResRetType -> Exp -> LiftM Exp liftFunction fname tparams params (RetType dims ret) funbody = do -- Find free variables- global <- gets stateGlobal- let bound =- global- <> foldMap patNames params- <> S.fromList (map typeParamName tparams)- <> S.fromList dims+ vtable <- asks envVtable+ let isFree v = (v,) <$> M.lookup v vtable+ withTypes = mapMaybe isFree . S.toList . fvVars - free =- let immediate_free = freeInExp funbody `freeWithout` (bound <> existentials funbody)- sizes_in_free =- foldMap freeInType $ M.elems $ unFV immediate_free+ let free =+ let immediate_free = withTypes $ freeInExp funbody+ sizes_in_free = foldMap (freeInType . snd) immediate_free sizes =- freeSizes $+ withTypes $ sizes_in_free <> foldMap freeInPat params <> freeInType ret- in M.toList $ unFV $ immediate_free <> (sizes `freeWithout` bound)+ in nubOrd $ immediate_free <> sizes -- Those parameters that correspond to sizes must come first. sizes_in_types =- foldMap freeInType (ret : map snd free ++ map patternStructType params)- isSize (v, _) = v `S.member` sizes_in_types+ foldMap freeInType (toStruct ret : map snd free ++ map patternStructType params)+ isSize (v, _) = v `S.member` fvVars sizes_in_types (free_dims, free_nondims) = partition isSize free - free_params =- map (mkParam . second (`setUniqueness` Nonunique)) $- free_dims ++ free_nondims+ free_ts = map (second (`setUniqueness` Nonunique)) $ free_dims ++ free_nondims addValBind $ ValBind { valBindName = fname, valBindTypeParams = tparams,- valBindParams = free_params ++ params,+ valBindParams = map mkParam free_ts ++ params, valBindRetDecl = Nothing, valBindRetType = Info (RetType dims ret), valBindBody = funbody,@@ -119,43 +126,54 @@ pure $ apply- (Var (qualName fname) (Info (augType $ free_dims ++ free_nondims)) mempty)+ (Var (qualName fname) (Info (augType free_ts)) mempty) $ free_dims ++ free_nondims where orig_type = funType params $ RetType dims ret- mkParam (v, t) = Id v (Info (fromStruct t)) mempty- freeVar (v, t) = Var (qualName v) (Info (fromStruct t)) mempty- augType rem_free = fromStruct $ funType (map mkParam rem_free) $ RetType [] orig_type+ mkParam (v, t) = Id v (Info (toParam Observe t)) mempty+ freeVar (v, t) = Var (qualName v) (Info t) mempty+ augType rem_free = funType (map mkParam rem_free) $ RetType [] $ toRet orig_type apply :: Exp -> [(VName, StructType)] -> Exp apply f [] = f apply f (p : rem_ps) =- let inner_ret = AppRes (fromStruct (augType rem_ps)) mempty+ let inner_ret = AppRes (augType rem_ps) mempty inner = mkApply f [(Observe, Nothing, freeVar p)] inner_ret in apply inner rem_ps +transformSubExps :: ASTMapper LiftM+transformSubExps = identityMapper {mapOnExp = transformExp}+ transformExp :: Exp -> LiftM Exp transformExp (AppExp (LetFun fname (tparams, params, _, Info ret, funbody) body _) _) = do- funbody' <- transformExp funbody+ funbody' <- bindingParams (map typeParamName tparams) params $ transformExp funbody fname' <- newVName $ "lifted_" ++ baseString fname lifted_call <- liftFunction fname' tparams params ret funbody' replacing fname lifted_call $ transformExp body-transformExp (Lambda params body _ (Info (_, ret)) _) = do- body' <- transformExp body+transformExp (Lambda params body _ (Info ret) _) = do+ body' <- bindingParams [] params $ transformExp body fname <- newVName "lifted_lambda" liftFunction fname [] params ret body'+transformExp (AppExp (LetPat sizes pat e body loc) appres) = do+ e' <- transformExp e+ body' <- bindingLetPat (map sizeName sizes) pat $ transformExp body+ pure $ AppExp (LetPat sizes pat e' body' loc) appres+transformExp (AppExp (DoLoop sizes pat args form body loc) appres) = do+ args' <- transformExp args+ form' <- astMap transformSubExps form+ body' <- bindingParams sizes [pat] $ bindingForm form' $ transformExp body+ pure $ AppExp (DoLoop sizes pat args' form' body' loc) appres transformExp e@(Var v _ _) = -- Note that function-typed variables can only occur in expressions, -- not in other places where VNames/QualNames can occur. asks (fromMaybe e . M.lookup (qualLeaf v) . envReplace)-transformExp e =- astMap m e- where- m = identityMapper {mapOnExp = transformExp}+transformExp e = astMap transformSubExps e transformValBind :: ValBind -> LiftM () transformValBind vb = do- e <- transformExp $ valBindBody vb+ e <-+ bindingParams (map typeParamName $ valBindTypeParams vb) (valBindParams vb) $+ transformExp (valBindBody vb) addValBind $ vb {valBindBody = e} {-# NOINLINE transformProg #-}
src/Futhark/Internalise/Monad.hs view
@@ -12,7 +12,6 @@ addOpaques, addFunDef, lookupFunction,- lookupFunction', lookupConst, bindFunction, bindConstant,@@ -36,7 +35,7 @@ ( [VName], [DeclType], [FParam SOACS],- [(SubExp, Type)] -> Maybe [DeclExtType]+ [(SubExp, Type)] -> Maybe [(DeclExtType, RetAls)] ) type FunTable = M.Map VName FunInfo@@ -145,11 +144,8 @@ addFunDef :: FunDef SOACS -> InternaliseM () addFunDef fd = modify $ \s -> s {stateFuns = fd : stateFuns s} -lookupFunction' :: VName -> InternaliseM (Maybe FunInfo)-lookupFunction' fname = gets $ M.lookup fname . stateFunTable- lookupFunction :: VName -> InternaliseM FunInfo-lookupFunction fname = maybe bad pure =<< lookupFunction' fname+lookupFunction fname = maybe bad pure =<< gets (M.lookup fname . stateFunTable) where bad = error $ "Internalise.lookupFunction: Function '" ++ prettyString fname ++ "' not found." @@ -176,7 +172,7 @@ letBindNames [cname] $ BasicOp $ SubExp se ses -> do let substs =- drop (length (shapeContext (funDefRetType fd))) ses+ drop (length (shapeContext (map fst (funDefRetType fd)))) ses modify $ \s -> s { stateConstSubsts = M.insert cname substs $ stateConstSubsts s
src/Futhark/Internalise/Monomorphise.hs view
@@ -6,9 +6,6 @@ -- -- * Turn operator sections into explicit lambdas. ----- * Converts identifiers of record type into record patterns (and--- similarly for tuples).--- -- * Converts applications of intrinsic SOACs into SOAC AST nodes -- (Map, Reduce, etc). --@@ -20,14 +17,19 @@ -- -- * Rewrite BinOp nodes to Apply nodes. --+-- * Replace all size expressions by constants or variables,+-- complex expressions replaced by variables are calculated in+-- let binding or replaced by size parameters if in argument.+-- -- Note that these changes are unfortunately not visible in the AST -- representation. module Futhark.Internalise.Monomorphise (transformProg) where import Control.Monad+import Control.Monad.Identity import Control.Monad.RWS (MonadReader (..), MonadWriter (..), RWST, asks, runRWST) import Control.Monad.State-import Control.Monad.Writer (runWriterT)+import Control.Monad.Writer (Writer, runWriter, runWriterT) import Data.Bifunctor import Data.Bitraversable import Data.Foldable@@ -38,6 +40,7 @@ import Data.Sequence qualified as Seq import Data.Set qualified as S import Futhark.MonadFreshNames+import Futhark.Util (nubOrd, topologicalSort) import Futhark.Util.Pretty import Language.Futhark import Language.Futhark.Semantic (TypeBinding (..))@@ -50,42 +53,97 @@ -- The monomorphization monad reads 'PolyBinding's and writes -- 'ValBind's. The 'TypeParam's in the 'ValBind's can only be size -- parameters.------ Each 'Polybinding' is also connected with the 'RecordReplacements'--- that were active when the binding was defined. This is used only--- in local functions.-data PolyBinding+newtype PolyBinding = PolyBinding- RecordReplacements ( VName, [TypeParam],- [Pat],- StructRetType,+ [Pat ParamType],+ ResRetType, Exp, [AttrInfo VName], SrcLoc ) --- Mapping from record names to the variable names that contain the--- fields. This is used because the monomorphiser also expands all--- record patterns.-type RecordReplacements = M.Map VName RecordReplacement+-- | To deduplicate size expressions, we want a looser notation of+-- equality than the strict syntactical equality provided by the Eq+-- instance on Exp. This newtype wrapper provides such a looser+-- notion of equality.+newtype ReplacedExp = ReplacedExp {unReplaced :: Exp}+ deriving (Show) -type RecordReplacement = M.Map Name (VName, PatType)+instance Pretty ReplacedExp where+ pretty (ReplacedExp e) = pretty e +instance Eq ReplacedExp where+ ReplacedExp e1 == ReplacedExp e2+ | Just es <- similarExps e1 e2 =+ all (uncurry (==) . bimap ReplacedExp ReplacedExp) es+ _ == _ = False++type ExpReplacements = [(ReplacedExp, VName)]++canCalculate :: S.Set VName -> ExpReplacements -> ExpReplacements+canCalculate scope mapping = do+ filter+ ( (`S.isSubsetOf` scope)+ . S.filter notIntrisic+ . fvVars+ . freeInExp+ . unReplaced+ . fst+ )+ mapping+ where+ notIntrisic vn = baseTag vn > maxIntrinsicTag++-- Replace some expressions by a parameter.+expReplace :: ExpReplacements -> Exp -> Exp+expReplace mapping e+ | Just vn <- lookup (ReplacedExp e) mapping =+ Var (qualName vn) (Info $ typeOf e) (srclocOf e)+expReplace mapping e = runIdentity $ astMap mapper e+ where+ mapper = identityMapper {mapOnExp = pure . expReplace mapping}++-- Construct an Assert expression that checks that the names (values)+-- in the mapping have the same value as the expression they+-- represent. This is injected into entry points, where we cannot+-- otherwise trust the input. XXX: the error message generated from+-- this is not great; we should rework it eventually.+entryAssert :: ExpReplacements -> Exp -> Exp+entryAssert [] body = body+entryAssert (x : xs) body =+ Assert (foldl logAnd (cmpExp x) $ map cmpExp xs) body errmsg (srclocOf body)+ where+ errmsg = Info "entry point arguments have invalid sizes."+ bool = Scalar $ Prim Bool+ opt = foldFunType [bool, bool] $ RetType [] bool+ andop = Var (qualName (intrinsicVar "&&")) (Info opt) mempty+ eqop = Var (qualName (intrinsicVar "==")) (Info opt) mempty+ logAnd x' y =+ mkApply andop [(Observe, Nothing, x'), (Observe, Nothing, y)] $+ AppRes bool []+ cmpExp (ReplacedExp x', y) =+ mkApply eqop [(Observe, Nothing, x'), (Observe, Nothing, y')] $+ AppRes bool []+ where+ y' = Var (qualName y) (Info i64) mempty+ -- Monomorphization environment mapping names of polymorphic functions -- to a representation of their corresponding function bindings. data Env = Env { envPolyBindings :: M.Map VName PolyBinding, envTypeBindings :: M.Map VName TypeBinding,- envRecordReplacements :: RecordReplacements+ envScope :: S.Set VName,+ envGlobalScope :: S.Set VName,+ envParametrized :: ExpReplacements } instance Semigroup Env where- Env tb1 pb1 rr1 <> Env tb2 pb2 rr2 = Env (tb1 <> tb2) (pb1 <> pb2) (rr1 <> rr2)+ Env tb1 pb1 sc1 gs1 pr1 <> Env tb2 pb2 sc2 gs2 pr2 = Env (tb1 <> tb2) (pb1 <> pb2) (sc1 <> sc2) (gs1 <> gs2) (pr1 <> pr2) instance Monoid Env where- mempty = Env mempty mempty mempty+ mempty = Env mempty mempty mempty mempty mempty localEnv :: Env -> MonoM a -> MonoM a localEnv env = local (env <>)@@ -95,19 +153,27 @@ localEnv mempty {envPolyBindings = M.singleton vn binding} -withRecordReplacements :: RecordReplacements -> MonoM a -> MonoM a-withRecordReplacements rr = localEnv mempty {envRecordReplacements = rr}+isolateNormalisation :: MonoM a -> MonoM a+isolateNormalisation m = do+ prevRepl <- get+ put mempty+ ret <- local (\env -> env {envScope = mempty, envParametrized = mempty}) m+ put prevRepl+ pure ret -replaceRecordReplacements :: RecordReplacements -> MonoM a -> MonoM a-replaceRecordReplacements rr = local $ \env -> env {envRecordReplacements = rr}+withArgs :: S.Set VName -> MonoM a -> MonoM a+withArgs args = localEnv $ mempty {envScope = args} +withParams :: ExpReplacements -> MonoM a -> MonoM a+withParams params = localEnv $ mempty {envParametrized = params}+ -- The monomorphization monad. newtype MonoM a = MonoM ( RWST Env (Seq.Seq (VName, ValBind))- VNameSource+ (ExpReplacements, VNameSource) (State Lifts) a )@@ -116,14 +182,21 @@ Applicative, Monad, MonadReader Env,- MonadWriter (Seq.Seq (VName, ValBind)),- MonadFreshNames+ MonadWriter (Seq.Seq (VName, ValBind)) ) +instance MonadFreshNames MonoM where+ getNameSource = MonoM $ gets snd+ putNameSource = MonoM . modify . second . const++instance MonadState ExpReplacements MonoM where+ get = MonoM $ gets fst+ put = MonoM . modify . first . const+ runMonoM :: VNameSource -> MonoM a -> ((a, Seq.Seq (VName, ValBind)), VNameSource) runMonoM src (MonoM m) = ((a, defs), src') where- (a, src', defs) = evalState (runRWST m mempty src) mempty+ (a, (_, src'), defs) = evalState (runRWST m mempty (mempty, src)) mempty lookupFun :: VName -> MonoM (Maybe PolyBinding) lookupFun vn = do@@ -132,28 +205,102 @@ Just valbind -> pure $ Just valbind Nothing -> pure Nothing -lookupRecordReplacement :: VName -> MonoM (Maybe RecordReplacement)-lookupRecordReplacement v = asks $ M.lookup v . envRecordReplacements+askScope :: MonoM (S.Set VName)+askScope = do+ scope <- asks envScope+ scope' <- asks $ S.union scope . envGlobalScope+ scope'' <- asks $ S.union scope' . M.keysSet . envPolyBindings+ S.union scope'' . S.fromList . map (fst . snd) <$> getLifts +-- | Asks the introduced variables in a set of argument,+-- that is arguments not currently in scope.+askIntros :: S.Set VName -> MonoM (S.Set VName)+askIntros argset =+ (S.filter notIntrisic argset `S.difference`) <$> askScope+ where+ notIntrisic vn = baseTag vn > maxIntrinsicTag++-- | Gets and removes expressions that could not be calculated when+-- the arguments set will be unscoped.+-- This should be called without argset in scope, for good detection of intros.+parametrizing :: S.Set VName -> MonoM ExpReplacements+parametrizing argset = do+ intros <- askIntros argset+ (params, nxtBind) <- gets $ partition (not . S.disjoint intros . fvVars . freeInExp . unReplaced . fst)+ put nxtBind+ pure params++calculateDims :: Exp -> ExpReplacements -> MonoM Exp+calculateDims body repl =+ foldCalc top_repl $ expReplace top_repl body+ where+ -- list of strict sub-expressions of e+ subExps e+ | Just e' <- stripExp e = subExps e'+ | otherwise = astMap mapper e `execState` mempty+ where+ mapOnExp e'+ | Just e'' <- stripExp e' = mapOnExp e''+ | otherwise = do+ modify (ReplacedExp e' :)+ astMap mapper e'+ mapper = identityMapper {mapOnExp}+ depends (a, _) (b, _) = b `elem` subExps (unReplaced a)+ top_repl =+ topologicalSort depends repl++ ---- Calculus insertion+ foldCalc [] body' = pure body'+ foldCalc ((dim, vn) : repls) body' = do+ reName <- newName vn+ let expr = expReplace repls $ unReplaced dim+ subst vn' =+ if vn' == vn+ then Just $ ExpSubst $ sizeFromName (qualName reName) mempty+ else Nothing+ appRes = case body' of+ (AppExp _ (Info (AppRes ty ext))) -> Info $ AppRes (applySubst subst ty) (reName : ext)+ e -> Info $ AppRes (applySubst subst $ typeOf e) [reName]+ foldCalc repls $+ AppExp+ ( LetPat+ []+ (Id vn (Info i64) (srclocOf expr))+ expr+ body'+ mempty+ )+ appRes++unscoping :: S.Set VName -> Exp -> MonoM Exp+unscoping argset body = do+ localDims <- parametrizing argset+ scope <- S.union argset <$> askScope+ calculateDims body $ canCalculate scope localDims++scoping :: S.Set VName -> MonoM Exp -> MonoM Exp+scoping argset m =+ withArgs argset m >>= unscoping argset+ -- Given instantiated type of function, produce size arguments.-type InferSizeArgs = StructType -> [Exp]+type InferSizeArgs = StructType -> MonoM [Exp] data MonoSize = -- | The integer encodes an equivalence class, so we can keep -- track of sizes that are statically identical. MonoKnown Int- | MonoAnon VName+ | MonoAnon deriving (Show) -- We treat all MonoAnon as identical. instance Eq MonoSize where MonoKnown x == MonoKnown y = x == y- MonoAnon _ == MonoAnon _ = True+ MonoAnon == MonoAnon = True _ == _ = False instance Pretty MonoSize where pretty (MonoKnown i) = "?" <> pretty i- pretty (MonoAnon v) = "?" <> prettyName v+ pretty MonoAnon = "?" instance Pretty (Shape MonoSize) where pretty (Shape ds) = mconcat (map (brackets . pretty) ds)@@ -161,14 +308,24 @@ -- The kind of type relative to which we monomorphise. What is most -- important to us is not the specific dimensions, but merely whether -- they are known or anonymous/local.-type MonoType = TypeBase MonoSize ()+type MonoType = TypeBase MonoSize NoUniqueness monoType :: TypeBase Size als -> MonoType-monoType = (`evalState` (0, mempty)) . traverseDims onDim . toStruct+monoType = noExts . (`evalState` (0, mempty)) . traverseDims onDim . toStruct where- onDim bound _ (NamedSize d)+ -- Remove exts from return types because we don't use them anymore.+ noExts :: TypeBase MonoSize u -> TypeBase MonoSize u+ noExts (Array u shape t) = Array u shape $ noExtsScalar t+ noExts (Scalar t) = Scalar $ noExtsScalar t+ noExtsScalar (Record fs) = Record $ M.map noExts fs+ noExtsScalar (Sum fs) = Sum $ M.map (map noExts) fs+ noExtsScalar (Arrow as p d t1 (RetType _ t2)) =+ Arrow as p d (noExts t1) (RetType [] (noExts t2))+ noExtsScalar t = t+ onDim bound _ e -- A locally bound size.- | qualLeaf d `S.member` bound = pure $ MonoAnon $ qualLeaf d+ | any (`S.member` bound) $ fvVars $ freeInExp e =+ pure MonoAnon onDim _ _ d = do (i, m) <- get case M.lookup d m of@@ -195,80 +352,125 @@ lookupLifted :: VName -> MonoType -> MonoM (Maybe (VName, InferSizeArgs)) lookupLifted fname t = lookup (fname, t) <$> getLifts +-- | Creates a new expression replacement if needed, this always produces normalised sizes.+-- (e.g. single variable or constant)+replaceExp :: Exp -> MonoM Exp+replaceExp e =+ case maybeNormalisedSize e of+ Just e' -> pure e'+ Nothing -> do+ let e' = ReplacedExp e+ prev <- gets $ lookup e'+ prev_param <- asks $ lookup e' . envParametrized+ case (prev_param, prev) of+ (Just vn, _) -> pure $ sizeFromName (qualName vn) (srclocOf e)+ (Nothing, Just vn) -> pure $ sizeFromName (qualName vn) (srclocOf e)+ (Nothing, Nothing) -> do+ vn <- newNameFromString $ "d<{" ++ prettyString (bareExp e) ++ "}>"+ modify ((e', vn) :)+ pure $ sizeFromName (qualName vn) (srclocOf e)+ where+ -- Avoid replacing of some 'already normalised' sizes that are just surounded by some parentheses.+ maybeNormalisedSize e'+ | Just e'' <- stripExp e' = maybeNormalisedSize e''+ maybeNormalisedSize (Var qn _ loc) = Just $ sizeFromName qn loc+ maybeNormalisedSize (IntLit v _ loc) = Just $ IntLit v (Info i64) loc+ maybeNormalisedSize _ = Nothing+ transformFName :: SrcLoc -> QualName VName -> StructType -> MonoM Exp-transformFName loc fname t- | baseTag (qualLeaf fname) <= maxIntrinsicTag = pure $ var fname- | otherwise = do- t' <- removeTypeVariablesInType t- let mono_t = monoType t'+transformFName loc fname t = do+ t' <- removeTypeVariablesInType t+ t'' <- transformType t'+ let mono_t = monoType t'+ if baseTag (qualLeaf fname) <= maxIntrinsicTag+ then pure $ var fname t''+ else do maybe_fname <- lookupLifted (qualLeaf fname) mono_t maybe_funbind <- lookupFun $ qualLeaf fname case (maybe_fname, maybe_funbind) of -- The function has already been monomorphised. (Just (fname', infer), _) ->- pure $ applySizeArgs fname' t' $ infer t'+ applySizeArgs fname' (toRes Nonunique t'') <$> infer t'' -- An intrinsic function.- (Nothing, Nothing) -> pure $ var fname+ (Nothing, Nothing) -> pure $ var fname t'' -- A polymorphic function. (Nothing, Just funbind) -> do (fname', infer, funbind') <- monomorphiseBinding False funbind mono_t tell $ Seq.singleton (qualLeaf fname, funbind') addLifted (qualLeaf fname) mono_t (fname', infer)- pure $ applySizeArgs fname' t' $ infer t'+ applySizeArgs fname' (toRes Nonunique t'') <$> infer t'' where- var fname' = Var fname' (Info (fromStruct t)) loc+ var fname' t'' = Var fname' (Info t'') loc - applySizeArg (i, f) size_arg =+ applySizeArg t' (i, f) size_arg = ( i - 1, mkApply f [(Observe, Nothing, size_arg)]- (AppRes (foldFunType (replicate i (Observe, i64)) (RetType [] (fromStruct t))) [])+ (AppRes (foldFunType (replicate i i64) (RetType [] t')) []) ) applySizeArgs fname' t' size_args = snd $ foldl'- applySizeArg+ (applySizeArg t') ( length size_args - 1, Var (qualName fname')- ( Info- ( foldFunType- (map (const (Observe, i64)) size_args)- (RetType [] $ fromStruct t')- )- )+ (Info (foldFunType (map (const i64) size_args) (RetType [] t'))) loc ) size_args --- This carries out record replacements in the alias information of a type.-transformType :: TypeBase dim Aliasing -> MonoM (TypeBase dim Aliasing)-transformType t = do- rrs <- asks envRecordReplacements- let replace (AliasBound v)- | Just d <- M.lookup v rrs =- S.fromList $ map (AliasBound . fst) $ M.elems d- replace x = S.singleton x- -- As an attempt at an optimisation, only transform the aliases if- -- they refer to a variable we have record-replaced.- pure $- if any ((`M.member` rrs) . aliasVar) $ aliases t- then second (mconcat . map replace . S.toList) t- else t+transformType :: TypeBase Size u -> MonoM (TypeBase Size u)+transformType typ =+ case typ of+ Scalar scalar -> Scalar <$> transformScalarSizes scalar+ Array u shape scalar -> Array u <$> mapM onDim shape <*> transformScalarSizes scalar+ where+ transformScalarSizes :: ScalarTypeBase Size u -> MonoM (ScalarTypeBase Size u)+ transformScalarSizes (Record fs) =+ Record <$> traverse transformType fs+ transformScalarSizes (Sum cs) =+ Sum <$> (traverse . traverse) transformType cs+ transformScalarSizes (Arrow as argName d argT retT) = do+ retT' <- transformRetTypeSizes argset retT+ Arrow as argName d <$> transformType argT <*> pure retT'+ where+ argset =+ fvVars (freeInType argT)+ <> case argName of+ Unnamed -> mempty+ Named vn -> S.singleton vn+ transformScalarSizes (TypeVar u qn args) =+ TypeVar u qn <$> mapM onArg args+ where+ onArg (TypeArgDim dim) = TypeArgDim <$> onDim dim+ onArg (TypeArgType ty) = TypeArgType <$> transformType ty+ transformScalarSizes ty@Prim {} = pure ty -sizesForPat :: MonadFreshNames m => Pat -> m ([VName], Pat)+ onDim e+ | e == anySize = pure e+ | otherwise = replaceExp =<< transformExp e++transformRetTypeSizes :: S.Set VName -> RetTypeBase Size as -> MonoM (RetTypeBase Size as)+transformRetTypeSizes argset (RetType dims ty) = do+ ty' <- withArgs argset $ transformType ty+ rl <- parametrizing argset+ let dims' = dims <> map snd rl+ pure $ RetType dims' ty'++sizesForPat :: MonadFreshNames m => Pat ParamType -> m ([VName], Pat ParamType) sizesForPat pat = do- (params', sizes) <- runStateT (astMap tv pat) []+ (params', sizes) <- runStateT (traverse (bitraverse onDim pure) pat) [] pure (sizes, params') where- tv = identityMapper {mapOnPatType = bitraverse onDim pure}- onDim (AnySize _) = do- v <- lift $ newVName "size"- modify (v :)- pure $ NamedSize $ qualName v- onDim d = pure d+ onDim d+ | d == anySize = do+ v <- lift $ newVName "size"+ modify (v :)+ pure $ sizeFromName (qualName v) mempty+ | otherwise = pure d transformAppRes :: AppRes -> MonoM AppRes transformAppRes (AppRes t ext) =@@ -279,64 +481,88 @@ e1' <- transformExp e1 me' <- mapM transformExp me incl' <- mapM transformExp incl- pure $ AppExp (Range e1' me' incl' loc) (Info res)-transformAppExp (Coerce e tp loc) res =- AppExp <$> (Coerce <$> transformExp e <*> pure tp <*> pure loc) <*> pure (Info res)-transformAppExp (LetPat sizes pat e1 e2 loc) res = do- (pat', rr) <- transformPat pat- AppExp- <$> ( LetPat sizes pat'- <$> transformExp e1- <*> withRecordReplacements rr (transformExp e2)- <*> pure loc- )- <*> pure (Info res)+ res' <- transformAppRes res+ pure $ AppExp (Range e1' me' incl' loc) (Info res')+transformAppExp (LetPat sizes pat e body loc) res = do+ e' <- transformExp e+ let dimArgs = S.fromList (map sizeName sizes)+ implicitDims <- withArgs dimArgs $ askIntros $ fvVars $ freeInPat pat+ let dimArgs' = dimArgs <> implicitDims+ letArgs = S.fromList $ patNames pat+ argset = dimArgs' `S.union` letArgs+ pat' <- withArgs dimArgs' $ transformPat pat+ params <- parametrizing dimArgs'+ let sizes' = sizes <> map (`SizeBinder` mempty) (map snd params <> S.toList implicitDims)+ body' <- withParams params $ scoping argset $ transformExp body+ res' <- transformAppRes res+ pure $ AppExp (LetPat sizes' pat' e' body' loc) (Info res') transformAppExp (LetFun fname (tparams, params, retdecl, Info ret, body) e loc) res | not $ null tparams = do- -- Retrieve the lifted monomorphic function bindings that are produced,- -- filter those that are monomorphic versions of the current let-bound- -- function and insert them at this point, and propagate the rest.- rr <- asks envRecordReplacements- let funbind = PolyBinding rr (fname, tparams, params, ret, body, mempty, loc)+ -- Retrieve the lifted monomorphic function bindings that are+ -- produced, filter those that are monomorphic versions of the+ -- current let-bound function and insert them at this point, and+ -- propagate the rest.+ let funbind = PolyBinding (fname, tparams, params, ret, body, mempty, loc) pass $ do- (e', bs) <- listen $ extendEnv fname funbind $ transformExp e+ (e', bs) <- listen $ extendEnv fname funbind $ scoping (S.singleton fname) $ transformExp e -- Do not remember this one for next time we monomorphise this -- function. modifyLifts $ filter ((/= fname) . fst . fst) let (bs_local, bs_prop) = Seq.partition ((== fname) . fst) bs pure (unfoldLetFuns (map snd $ toList bs_local) e', const bs_prop) | otherwise = do- body' <- transformExp body+ body' <- scoping (S.fromList (foldMap patNames params)) $ transformExp body+ ret' <- transformRetTypeSizes (S.fromList (foldMap patNames params)) ret AppExp- <$> (LetFun fname (tparams, params, retdecl, Info ret, body') <$> transformExp e <*> pure loc)- <*> pure (Info res)+ <$> ( LetFun fname (tparams, params, retdecl, Info ret', body')+ <$> scoping (S.singleton fname) (transformExp e)+ <*> pure loc+ )+ <*> (Info <$> transformAppRes res) transformAppExp (If e1 e2 e3 loc) res =- AppExp <$> (If <$> transformExp e1 <*> transformExp e2 <*> transformExp e3 <*> pure loc) <*> pure (Info res)+ AppExp <$> (If <$> transformExp e1 <*> transformExp e2 <*> transformExp e3 <*> pure loc) <*> (Info <$> transformAppRes res) transformAppExp (Apply fe args _) res = mkApply <$> transformExp fe <*> mapM onArg (NE.toList args)- <*> pure res+ <*> transformAppRes res where onArg (Info (d, ext), e) = (d,ext,) <$> transformExp e-transformAppExp (DoLoop sparams pat e1 form e3 loc) res = do+transformAppExp (DoLoop sparams pat e1 form body loc) res = do e1' <- transformExp e1- form' <- case form of- For ident e2 -> For ident <$> transformExp e2- ForIn pat2 e2 -> ForIn pat2 <$> transformExp e2- While e2 -> While <$> transformExp e2- e3' <- transformExp e3++ let dimArgs = S.fromList sparams+ pat' <- withArgs dimArgs $ transformPat pat+ params <- parametrizing dimArgs+ let sparams' = sparams <> map snd params+ mergeArgs = dimArgs `S.union` S.fromList (patNames pat)++ (form', formArgs) <- case form of+ For ident e2 -> (,S.singleton $ identName ident) . For ident <$> transformExp e2+ ForIn pat2 e2 -> do+ pat2' <- transformPat pat2+ (,S.fromList (patNames pat2)) . ForIn pat2' <$> transformExp e2+ While e2 ->+ fmap ((,mempty) . While) $+ withParams params $+ scoping mergeArgs $+ transformExp e2+ let argset = mergeArgs `S.union` formArgs++ body' <- withParams params $ scoping argset $ transformExp body -- Maybe monomorphisation introduced new arrays to the loop, and -- maybe they have AnySize sizes. This is not allowed. Invent some -- sizes for them.- (pat_sizes, pat') <- sizesForPat pat- pure $ AppExp (DoLoop (sparams ++ pat_sizes) pat' e1' form' e3' loc) (Info res)-transformAppExp (BinOp (fname, _) (Info t) (e1, d1) (e2, d2) loc) (AppRes ret ext) = do- fname' <- transformFName loc fname $ toStruct t+ (pat_sizes, pat'') <- sizesForPat pat'+ res' <- transformAppRes res+ pure $ AppExp (DoLoop (sparams' ++ pat_sizes) pat'' e1' form' body' loc) (Info res')+transformAppExp (BinOp (fname, _) (Info t) (e1, d1) (e2, d2) loc) res = do+ (AppRes ret ext) <- transformAppRes res+ fname' <- transformFName loc fname (toStruct t) e1' <- transformExp e1 e2' <- transformExp e2 if orderZero (typeOf e1') && orderZero (typeOf e2')- then pure $ applyOp fname' e1' e2'+ then pure $ applyOp ret ext fname' e1' e2' else do -- We have to flip the arguments to the function, because -- operator application is left-to-right, while function@@ -356,17 +582,17 @@ x_param e1' ( AppExp- (LetPat [] y_param e2' (applyOp fname' x_param_e y_param_e) loc)+ (LetPat [] y_param e2' (applyOp ret ext fname' x_param_e y_param_e) loc) (Info $ AppRes ret mempty) ) mempty ) (Info (AppRes ret mempty)) where- applyOp fname' x y =+ applyOp ret ext fname' x y = mkApply- (mkApply fname' [(Observe, snd (unInfo d1), x)] (AppRes ret mempty))- [(Observe, snd (unInfo d2), y)]+ (mkApply fname' [(Observe, unInfo d1, x)] (AppRes ret mempty))+ [(Observe, unInfo d2, y)] (AppRes ret ext) makeVarParam arg = do@@ -374,21 +600,45 @@ x <- newNameFromString "binop_p" pure ( Var (qualName x) (Info argtype) mempty,- Id x (Info $ fromStruct argtype) mempty+ Id x (Info argtype) mempty ) transformAppExp (LetWith id1 id2 idxs e1 body loc) res = do+ id1' <- transformIdent id1+ id2' <- transformIdent id2 idxs' <- mapM transformDimIndex idxs e1' <- transformExp e1- body' <- transformExp body- pure $ AppExp (LetWith id1 id2 idxs' e1' body' loc) (Info res)+ body' <- scoping (S.singleton $ identName id1') $ transformExp body+ res' <- transformAppRes res+ pure $ AppExp (LetWith id1' id2' idxs' e1' body' loc) (Info res')+ where+ transformIdent (Ident v t vloc) =+ Ident v <$> traverse transformType t <*> pure vloc transformAppExp (Index e0 idxs loc) res = AppExp <$> (Index <$> transformExp e0 <*> mapM transformDimIndex idxs <*> pure loc)- <*> pure (Info res)-transformAppExp (Match e cs loc) res =- AppExp- <$> (Match <$> transformExp e <*> mapM transformCase cs <*> pure loc)- <*> pure (Info res)+ <*> (Info <$> transformAppRes res)+transformAppExp (Match e cs loc) res = do+ implicitDims <- askIntros $ fvVars $ freeInType $ typeOf e+ e' <- transformExp e+ cs' <- mapM (transformCase implicitDims) cs+ res' <- transformAppRes res+ if S.null implicitDims+ then pure $ AppExp (Match e' cs' loc) (Info res')+ else do+ tmpVar <- newNameFromString "matched_variable"+ pure $+ AppExp+ ( LetPat+ (map (`SizeBinder` mempty) $ S.toList implicitDims)+ (Id tmpVar (Info $ typeOf e') mempty)+ e'+ ( AppExp+ (Match (Var (qualName tmpVar) (Info $ typeOf e') mempty) cs' loc)+ (Info res)+ )+ mempty+ )+ (Info res') -- Monomorphization of expressions. transformExp :: Exp -> MonoM Exp@@ -416,40 +666,38 @@ loc transformExp (ArrayLit es t loc) = ArrayLit <$> mapM transformExp es <*> traverse transformType t <*> pure loc-transformExp (AppExp e res) = do- noticeDims $ appResType $ unInfo res- transformAppExp e =<< transformAppRes (unInfo res)-transformExp (Var fname (Info t) loc) = do- maybe_fs <- lookupRecordReplacement $ qualLeaf fname- case maybe_fs of- Just fs -> do- let toField (f, (f_v, f_t)) = do- f_t' <- transformType f_t- let f_v' = Var (qualName f_v) (Info f_t') loc- pure $ RecordFieldExplicit f f_v' loc- RecordLit <$> mapM toField (M.toList fs) <*> pure loc- Nothing -> do- t' <- transformType t- transformFName loc fname (toStruct t')+transformExp (AppExp e res) =+ transformAppExp e (unInfo res)+transformExp (Var fname (Info t) loc) =+ transformFName loc fname (toStruct t) transformExp (Hole t loc) = Hole <$> traverse transformType t <*> pure loc transformExp (Ascript e tp loc) = Ascript <$> transformExp e <*> pure tp <*> pure loc+transformExp (Coerce e te t loc) =+ Coerce <$> transformExp e <*> pure te <*> traverse transformType t <*> pure loc transformExp (Negate e loc) = Negate <$> transformExp e <*> pure loc transformExp (Not e loc) = Not <$> transformExp e <*> pure loc transformExp (Lambda params e0 decl tp loc) = do- e0' <- transformExp e0- pure $ Lambda params e0' decl tp loc+ let patArgs = S.fromList $ foldMap patNames params+ dimArgs <- withArgs patArgs $ askIntros (foldMap (fvVars . freeInPat) params)+ let argset = dimArgs `S.union` patArgs+ params' <- mapM transformPat params+ paramed <- parametrizing argset+ Lambda params'+ <$> withParams paramed (scoping argset $ transformExp e0)+ <*> pure decl+ <*> traverse transformRetType tp+ <*> pure loc transformExp (OpSection qn t loc) = transformExp $ Var qn t loc transformExp (OpSectionLeft fname (Info t) e arg (Info rettype, Info retext) loc) = do let (Info (xp, xtype, xargext), Info (yp, ytype)) = arg- fname' <- transformFName loc fname $ toStruct t e' <- transformExp e desugarBinOpSection- fname'+ fname (Just e') Nothing t@@ -459,10 +707,9 @@ loc transformExp (OpSectionRight fname (Info t) e arg (Info rettype) loc) = do let (Info (xp, xtype), Info (yp, ytype, yargext)) = arg- fname' <- transformFName loc fname $ toStruct t e' <- transformExp e desugarBinOpSection- fname'+ fname Nothing (Just e') t@@ -470,22 +717,16 @@ (yp, ytype, yargext) (rettype, []) loc-transformExp (ProjectSection fields (Info t) loc) =- desugarProjectSection fields t loc+transformExp (ProjectSection fields (Info t) loc) = do+ t' <- transformType t+ desugarProjectSection fields t' loc transformExp (IndexSection idxs (Info t) loc) = do idxs' <- mapM transformDimIndex idxs desugarIndexSection idxs' t loc transformExp (Project n e tp loc) = do- maybe_fs <- case e of- Var qn _ _ -> lookupRecordReplacement (qualLeaf qn)- _ -> pure Nothing- case maybe_fs of- Just m- | Just (v, _) <- M.lookup n m ->- pure $ Var (qualName v) tp loc- _ -> do- e' <- transformExp e- pure $ Project n e' tp loc+ tp' <- traverse transformType tp+ e' <- transformExp e+ pure $ Project n e' tp' loc transformExp (Update e1 idxs e2 loc) = Update <$> transformExp e1@@ -497,19 +738,19 @@ <$> transformExp e1 <*> pure fs <*> transformExp e2- <*> pure t+ <*> traverse transformType t <*> pure loc transformExp (Assert e1 e2 desc loc) = Assert <$> transformExp e1 <*> transformExp e2 <*> pure desc <*> pure loc transformExp (Constr name all_es t loc) =- Constr name <$> mapM transformExp all_es <*> pure t <*> pure loc+ Constr name <$> mapM transformExp all_es <*> traverse transformType t <*> pure loc transformExp (Attr info e loc) = Attr info <$> transformExp e <*> pure loc -transformCase :: Case -> MonoM Case-transformCase (CasePat p e loc) = do- (p', rr) <- transformPat p- CasePat p' <$> withRecordReplacements rr (transformExp e) <*> pure loc+transformCase :: S.Set VName -> Case -> MonoM Case+transformCase implicitDims (CasePat p e loc) = do+ p' <- transformPat p+ CasePat p' <$> scoping (S.fromList (patNames p) `S.union` implicitDims) (transformExp e) <*> pure loc transformDimIndex :: DimIndexBase Info VName -> MonoM (DimIndexBase Info VName) transformDimIndex (DimFix e) = DimFix <$> transformExp e@@ -520,67 +761,67 @@ -- Transform an operator section into a lambda. desugarBinOpSection ::- Exp ->+ QualName VName -> Maybe Exp -> Maybe Exp ->- PatType ->- (PName, StructType, Maybe VName) ->- (PName, StructType, Maybe VName) ->- (PatRetType, [VName]) ->+ StructType ->+ (PName, ParamType, Maybe VName) ->+ (PName, ParamType, Maybe VName) ->+ (ResRetType, [VName]) -> SrcLoc -> MonoM Exp-desugarBinOpSection op e_left e_right t (xp, xtype, xext) (yp, ytype, yext) (RetType dims rettype, retext) loc = do- (v1, wrap_left, e1, p1) <- makeVarParam e_left $ fromStruct xtype- (v2, wrap_right, e2, p2) <- makeVarParam e_right $ fromStruct ytype+desugarBinOpSection fname e_left e_right t (xp, xtype, xext) (yp, ytype, yext) (RetType dims rettype, retext) loc = do+ t' <- transformType t+ op <- transformFName loc fname $ toStruct t+ (v1, wrap_left, e1, p1) <- makeVarParam e_left =<< transformType xtype+ (v2, wrap_right, e2, p2) <- makeVarParam e_right =<< transformType ytype let apply_left = mkApply op [(Observe, xext, e1)]- (AppRes (Scalar $ Arrow mempty yp Observe ytype (RetType [] t)) [])- rettype' =- let onDim (NamedSize d)- | Named p <- xp, qualLeaf d == p = NamedSize $ qualName v1- | Named p <- yp, qualLeaf d == p = NamedSize $ qualName v2- onDim d = d- in first onDim rettype- body =- mkApply apply_left [(Observe, yext, e2)] (AppRes rettype' retext)- rettype'' = toStruct rettype'- pure $- wrap_left $- wrap_right $- Lambda (p1 ++ p2) body Nothing (Info (mempty, RetType dims rettype'')) loc+ (AppRes (Scalar $ Arrow mempty yp (diet ytype) (toStruct ytype) (RetType [] $ toRes Nonunique t')) [])+ onDim (Var d typ _)+ | Named p <- xp, qualLeaf d == p = Var (qualName v1) typ loc+ | Named p <- yp, qualLeaf d == p = Var (qualName v2) typ loc+ onDim d = d+ rettype' = first onDim rettype+ body <-+ scoping (S.fromList [v1, v2]) $+ mkApply apply_left [(Observe, yext, e2)]+ <$> transformAppRes (AppRes (toStruct rettype') retext)+ rettype'' <- transformRetTypeSizes (S.fromList [v1, v2]) $ RetType dims rettype'+ pure . wrap_left . wrap_right $+ Lambda (p1 ++ p2) body Nothing (Info rettype'') loc where patAndVar argtype = do x <- newNameFromString "x" pure ( x, Id x (Info argtype) mempty,- Var (qualName x) (Info argtype) mempty+ Var (qualName x) (Info (toStruct argtype)) mempty ) makeVarParam (Just e) argtype = do (v, pat, var_e) <- patAndVar argtype let wrap body =- AppExp (LetPat [] pat e body mempty) (Info $ AppRes (typeOf body) mempty)+ AppExp (LetPat [] (fmap toStruct pat) e body mempty) (Info $ AppRes (typeOf body) mempty) pure (v, wrap, var_e, []) makeVarParam Nothing argtype = do (v, pat, var_e) <- patAndVar argtype pure (v, id, var_e, [pat]) -desugarProjectSection :: [Name] -> PatType -> SrcLoc -> MonoM Exp+desugarProjectSection :: [Name] -> StructType -> SrcLoc -> MonoM Exp desugarProjectSection fields (Scalar (Arrow _ _ _ t1 (RetType dims t2))) loc = do p <- newVName "project_p"- let body = foldl project (Var (qualName p) (Info t1') mempty) fields+ let body = foldl project (Var (qualName p) (Info t1) mempty) fields pure $ Lambda- [Id p (Info t1') mempty]+ [Id p (Info $ toParam Observe t1) mempty] body Nothing- (Info (mempty, RetType dims $ toStruct t2))+ (Info (RetType dims t2)) loc where- t1' = fromStruct t1 project e field = case typeOf e of Scalar (Record fs)@@ -594,26 +835,21 @@ ++ prettyString field desugarProjectSection _ t _ = error $ "desugarOpSection: not a function type: " ++ prettyString t -desugarIndexSection :: [DimIndex] -> PatType -> SrcLoc -> MonoM Exp+desugarIndexSection :: [DimIndex] -> StructType -> SrcLoc -> MonoM Exp desugarIndexSection idxs (Scalar (Arrow _ _ _ t1 (RetType dims t2))) loc = do p <- newVName "index_i"- let body = AppExp (Index (Var (qualName p) (Info t1') loc) idxs loc) (Info (AppRes t2 []))+ t1' <- transformType t1+ t2' <- transformType t2+ let body = AppExp (Index (Var (qualName p) (Info t1') loc) idxs loc) (Info (AppRes (toStruct t2') [])) pure $ Lambda- [Id p (Info (fromStruct t1')) mempty]+ [Id p (Info $ toParam Observe t1') mempty] body Nothing- (Info (mempty, RetType dims $ toStruct t2))+ (Info (RetType dims t2')) loc- where- t1' = fromStruct t1 desugarIndexSection _ t _ = error $ "desugarIndexSection: not a function type: " ++ prettyString t -noticeDims :: TypeBase Size as -> MonoM ()-noticeDims = mapM_ notice . freeInType- where- notice v = void $ transformFName mempty (qualName v) i64- -- Convert a collection of 'ValBind's to a nested sequence of let-bound, -- monomorphic functions with the given expression at the bottom. unfoldLetFuns :: [ValBind] -> Exp -> Exp@@ -624,47 +860,8 @@ e' = unfoldLetFuns rest e e_t = typeOf e' -transformPat :: Pat -> MonoM (Pat, RecordReplacements)-transformPat (Id v (Info (Scalar (Record fs))) loc) = do- let fs' = M.toList fs- (fs_ks, fs_ts) <- fmap unzip $- forM fs' $ \(f, ft) ->- (,) <$> newVName (nameToString f) <*> transformType ft- pure- ( RecordPat- (zip (map fst fs') (zipWith3 Id fs_ks (map Info fs_ts) $ repeat loc))- loc,- M.singleton v $ M.fromList $ zip (map fst fs') $ zip fs_ks fs_ts- )-transformPat (Id v t loc) = pure (Id v t loc, mempty)-transformPat (TuplePat pats loc) = do- (pats', rrs) <- mapAndUnzipM transformPat pats- pure (TuplePat pats' loc, mconcat rrs)-transformPat (RecordPat fields loc) = do- let (field_names, field_pats) = unzip fields- (field_pats', rrs) <- mapAndUnzipM transformPat field_pats- pure (RecordPat (zip field_names field_pats') loc, mconcat rrs)-transformPat (PatParens pat loc) = do- (pat', rr) <- transformPat pat- pure (PatParens pat' loc, rr)-transformPat (PatAttr attr pat loc) = do- (pat', rr) <- transformPat pat- pure (PatAttr attr pat' loc, rr)-transformPat (Wildcard (Info t) loc) = do- t' <- transformType t- pure (wildcard t' loc, mempty)-transformPat (PatAscription pat _ _) =- transformPat pat-transformPat (PatLit e t loc) = pure (PatLit e t loc, mempty)-transformPat (PatConstr name t all_ps loc) = do- (all_ps', rrs) <- mapAndUnzipM transformPat all_ps- pure (PatConstr name t all_ps' loc, mconcat rrs)--wildcard :: PatType -> SrcLoc -> Pat-wildcard (Scalar (Record fs)) loc =- RecordPat (zip (M.keys fs) $ map ((`Wildcard` loc) . Info) $ M.elems fs) loc-wildcard t loc =- Wildcard (Info t) loc+transformPat :: Pat (TypeBase Size u) -> MonoM (Pat (TypeBase Size u))+transformPat = traverse transformType type DimInst = M.Map VName Size @@ -672,28 +869,46 @@ Monoid a => TypeBase Size a -> TypeBase Size a ->+ ExpReplacements ->+ ExpReplacements -> DimInst-dimMapping t1 t2 = execState (matchDims f t1 t2) mempty+dimMapping t1 t2 r1 r2 = execState (matchDims onDims t1 t2) mempty where- f bound d1 (NamedSize d2)- | qualLeaf d2 `elem` bound = pure d1- f _ (NamedSize d1) d2 = do- modify $ M.insert (qualLeaf d1) d2- pure $ NamedSize d1- f _ d _ = pure d+ revMap = map (\(k, v) -> (v, k))+ named1 = revMap r1+ named2 = revMap r2 -inferSizeArgs :: [TypeParam] -> StructType -> StructType -> [Exp]-inferSizeArgs tparams bind_t t =- mapMaybe (tparamArg (dimMapping bind_t t)) tparams+ onDims bound e1 e2 = do+ onExps bound e1 e2+ pure e1++ onExps bound (Var v _ _) e = do+ unless (any (`elem` bound) $ freeVarsInExp e) $+ modify (M.insert (qualLeaf v) e)+ case lookup (qualLeaf v) named1 of+ Just rexp -> onExps bound (unReplaced rexp) e+ Nothing -> pure ()+ onExps bound e (Var v _ _)+ | Just rexp <- lookup (qualLeaf v) named2 = onExps bound e (unReplaced rexp)+ onExps bound e1 e2+ | Just es <- similarExps e1 e2 =+ mapM_ (uncurry $ onExps bound) es+ onExps _ _ _ = pure mempty++ freeVarsInExp = fvVars . freeInExp++inferSizeArgs :: [TypeParam] -> StructType -> ExpReplacements -> StructType -> MonoM [Exp]+inferSizeArgs tparams bind_t bind_r t = do+ r <- get+ let dinst = dimMapping bind_t t bind_r r+ mapM (tparamArg dinst) tparams where tparamArg dinst tp = case M.lookup (typeParamName tp) dinst of- Just (NamedSize d) ->- Just $ Var d (Info i64) mempty- Just (ConstSize x) ->- Just $ Literal (SignedValue $ Int64Value $ fromIntegral x) mempty- _ ->- Just $ Literal (SignedValue $ Int64Value 0) mempty+ Just e ->+ replaceExp e+ Nothing ->+ pure $ sizeFromInteger 0 mempty -- Monomorphising higher-order functions can result in function types -- where the same named parameter occurs in multiple spots. When@@ -705,16 +920,109 @@ noNamedParams :: MonoType -> MonoType noNamedParams = f where- f (Array () u shape t) = Array () u shape (f' t)+ f :: TypeBase MonoSize u -> TypeBase MonoSize u+ f (Array u shape t) = Array u shape (f' t) f (Scalar t) = Scalar $ f' t- f' (Arrow () _ d1 t1 (RetType dims t2)) =- Arrow () Unnamed d1 (f t1) (RetType dims (f t2))- f' (Record fs) =- Record $ fmap f fs- f' (Sum cs) =- Sum $ fmap (map f) cs+ f' :: ScalarTypeBase MonoSize u -> ScalarTypeBase MonoSize u+ f' (Record fs) = Record $ fmap f fs+ f' (Sum cs) = Sum $ fmap (map f) cs+ f' (Arrow u _ d1 t1 (RetType dims t2)) =+ Arrow u Unnamed d1 (f t1) (RetType dims (f t2)) f' t = t +transformRetType :: RetTypeBase Size u -> MonoM (RetTypeBase Size u)+transformRetType (RetType ext t) = RetType ext <$> transformType t++-- | arrowArg takes a return type and returns it+-- with the existentials bound moved at the right of arrows.+-- It also gives the new set of parameters to consider.+arrowArg ::+ S.Set VName -> -- scope+ S.Set VName -> -- set of argument+ [VName] -> -- size parameters+ RetTypeBase Size as ->+ (RetTypeBase Size as, S.Set VName)+arrowArg scope argset args_params rety =+ let (rety', (funArgs, _)) = runWriter (arrowArgRetType (scope, mempty) argset rety)+ new_params = funArgs `S.union` S.fromList args_params+ in (arrowCleanRetType new_params rety', new_params)+ where+ -- \| takes a type (or return type) and returns it+ -- with the existentials bound moved at the right of arrows.+ -- It also gives (through writer monad) size variables used in arrow arguments+ -- and variables that are constructively used.+ -- The returned type should be cleanned, as too many existentials are introduced.+ arrowArgRetType ::+ (S.Set VName, [VName]) ->+ S.Set VName ->+ RetTypeBase Size as' ->+ Writer (S.Set VName, S.Set VName) (RetTypeBase Size as')+ arrowArgRetType (scope', dimsToPush) argset' (RetType dims ty) = pass $ do+ let dims' = dims <> dimsToPush+ (ty', (_, canExt)) <- listen $ arrowArgType (argset' `S.union` scope', dims') ty+ pure (RetType (filter (`S.member` canExt) dims') ty', first (`S.difference` canExt))++ arrowArgScalar env (Record fs) =+ Record <$> traverse (arrowArgType env) fs+ arrowArgScalar env (Sum cs) =+ Sum <$> (traverse . traverse) (arrowArgType env) cs+ arrowArgScalar (scope', dimsToPush) (Arrow as argName d argT retT) =+ pass $ do+ let intros = S.filter notIntrisic argset' `S.difference` scope'+ retT' <- arrowArgRetType (scope', filter (`S.notMember` intros) dimsToPush) fullArgset retT+ pure (Arrow as argName d argT retT', bimap (intros `S.union`) (const mempty))+ where+ notIntrisic vn = baseTag vn > maxIntrinsicTag+ argset' = fvVars $ freeInType argT+ fullArgset =+ argset'+ <> case argName of+ Unnamed -> mempty+ Named vn -> S.singleton vn+ arrowArgScalar env (TypeVar u qn args) =+ TypeVar u qn <$> mapM arrowArgArg args+ where+ arrowArgArg (TypeArgDim dim) = TypeArgDim <$> arrowArgSize dim+ arrowArgArg (TypeArgType ty) = TypeArgType <$> arrowArgType env ty+ arrowArgScalar _ ty = pure ty++ arrowArgType ::+ (S.Set VName, [VName]) ->+ TypeBase Size as' ->+ Writer (S.Set VName, S.Set VName) (TypeBase Size as')+ arrowArgType env (Array u shape scalar) =+ Array u <$> traverse arrowArgSize shape <*> arrowArgScalar env scalar+ arrowArgType env (Scalar ty) =+ Scalar <$> arrowArgScalar env ty++ arrowArgSize s@(Var qn _ _) = writer (s, (mempty, S.singleton $ qualLeaf qn))+ arrowArgSize s = pure s++ -- \| arrowClean cleans the mess in the type+ arrowCleanRetType :: S.Set VName -> RetTypeBase Size as -> RetTypeBase Size as+ arrowCleanRetType paramed (RetType dims ty) =+ RetType (nubOrd $ filter (`S.notMember` paramed) dims) (arrowCleanType (paramed `S.union` S.fromList dims) ty)++ arrowCleanScalar :: S.Set VName -> ScalarTypeBase Size as -> ScalarTypeBase Size as+ arrowCleanScalar paramed (Record fs) =+ Record $ M.map (arrowCleanType paramed) fs+ arrowCleanScalar paramed (Sum cs) =+ Sum $ (M.map . map) (arrowCleanType paramed) cs+ arrowCleanScalar paramed (Arrow as argName d argT retT) =+ Arrow as argName d argT (arrowCleanRetType paramed retT)+ arrowCleanScalar paramed (TypeVar u qn args) =+ TypeVar u qn $ map arrowCleanArg args+ where+ arrowCleanArg (TypeArgDim dim) = TypeArgDim dim+ arrowCleanArg (TypeArgType ty) = TypeArgType $ arrowCleanType paramed ty+ arrowCleanScalar _ ty = ty++ arrowCleanType :: S.Set VName -> TypeBase Size as -> TypeBase Size as+ arrowCleanType paramed (Array u shape scalar) =+ Array u shape $ arrowCleanScalar paramed scalar+ arrowCleanType paramed (Scalar ty) =+ Scalar $ arrowCleanScalar paramed ty+ -- Monomorphise a polymorphic function at the types given in the instance -- list. Monomorphises the body of the function as well. Returns the fresh name -- of the generated monomorphic function and its 'ValBind' representation.@@ -723,26 +1031,53 @@ PolyBinding -> MonoType -> MonoM (VName, InferSizeArgs, ValBind)-monomorphiseBinding entry (PolyBinding rr (name, tparams, params, rettype, body, attrs, loc)) inst_t =- replaceRecordReplacements rr $ do+monomorphiseBinding entry (PolyBinding (name, tparams, params, rettype, body, attrs, loc)) inst_t = do+ letFun <- asks $ S.member name . envScope+ let paramGetClean argset =+ if letFun+ then parametrizing argset+ else do+ ret <- get+ put mempty+ pure ret+ (if letFun then id else isolateNormalisation) $ do let bind_t = funType params rettype- (substs, t_shape_params) <- typeSubstsM loc (noSizes bind_t) $ noNamedParams inst_t- let substs' = M.map (Subst []) substs- rettype' = applySubst (`M.lookup` substs') rettype- substPatType =+ (substs, t_shape_params) <-+ typeSubstsM loc (noSizes bind_t) $ noNamedParams inst_t+ let shape_names = S.fromList $ map typeParamName $ shape_params ++ t_shape_params+ substs' = M.map (Subst []) substs+ substStructType = substTypesAny (fmap (fmap (second (const mempty))) . (`M.lookup` substs'))- params' = map (substPat entry substPatType) params- bind_t' = substTypesAny (`M.lookup` substs') bind_t- (shape_params_explicit, shape_params_implicit) =- partition ((`S.member` mustBeExplicitInBinding bind_t') . typeParamName) $- shape_params ++ t_shape_params+ params' = map (substPat entry substStructType) params+ params'' <- withArgs shape_names $ mapM transformPat params'+ exp_naming <- paramGetClean shape_names - (params'', rrs) <- mapAndUnzipM transformPat params'+ let args = S.fromList $ foldMap patNames params+ arg_params = map snd exp_naming - mapM_ noticeDims $ retType rettype : map patternStructType params''+ rettype' <-+ withParams exp_naming $+ withArgs (args <> shape_names) $+ hardTransformRetType (applySubst (`M.lookup` substs') rettype)+ extNaming <- paramGetClean (args <> shape_names)+ scope <- S.union shape_names <$> askScope'+ let (rettype'', new_params) = arrowArg scope args arg_params rettype'+ bind_t' = substTypesAny (`M.lookup` substs') bind_t+ (shape_params_explicit, shape_params_implicit) =+ partition ((`S.member` (mustBeExplicitInBinding bind_t'' `S.union` mustBeExplicitInBinding bind_t')) . typeParamName) $+ shape_params ++ t_shape_params ++ map (`TypeParamDim` mempty) (S.toList new_params)+ exp_naming' = filter ((`S.member` new_params) . snd) (extNaming <> exp_naming) + bind_t'' = funType params'' rettype''+ bind_r = exp_naming <> extNaming body' <- updateExpTypes (`M.lookup` substs') body- body'' <- withRecordReplacements (mconcat rrs) $ transformExp body'+ body'' <- withParams exp_naming' $ withArgs (shape_names <> args) $ transformExp body'+ scope' <- S.union (shape_names <> args) <$> askScope'+ body''' <-+ if letFun+ then unscoping (shape_names <> args) body''+ else expReplace exp_naming' <$> (calculateDims body'' . canCalculate scope' =<< get)+ seen_before <- elem name . map (fst . fst) <$> getLifts name' <- if null tparams && not entry && not seen_before@@ -751,36 +1086,43 @@ pure ( name',- inferSizeArgs shape_params_explicit bind_t',+ inferSizeArgs shape_params_explicit bind_t'' bind_r, if entry then toValBinding name' (shape_params_explicit ++ shape_params_implicit) params''- rettype'- body''+ rettype''+ (entryAssert exp_naming body''') else toValBinding name' shape_params_implicit (map shapeParam shape_params_explicit ++ params'')- rettype'- body''+ rettype''+ body''' ) where+ askScope' = S.filter (`notElem` retDims rettype) <$> askScope+ shape_params = filter (not . isTypeParam) tparams updateExpTypes substs = astMap (mapper substs) + hardTransformRetType (RetType dims ty) = do+ ty' <- transformType ty+ unbounded <- askIntros $ fvVars $ freeInType ty'+ let dims' = S.toList unbounded+ pure $ RetType (dims' <> dims) ty'+ mapper substs = ASTMapper { mapOnExp = updateExpTypes substs, mapOnName = pure, mapOnStructType = pure . applySubst substs,- mapOnPatType = pure . applySubst substs,- mapOnStructRetType = pure . applySubst substs,- mapOnPatRetType = pure . applySubst substs+ mapOnParamType = pure . applySubst substs,+ mapOnResRetType = pure . applySubst substs } shapeParam tp = Id (typeParamName tp) (Info i64) $ srclocOf tp@@ -802,13 +1144,13 @@ typeSubstsM :: MonadFreshNames m => SrcLoc ->- TypeBase () () ->+ TypeBase () NoUniqueness -> MonoType -> m (M.Map VName StructRetType, [TypeParam]) typeSubstsM loc orig_t1 orig_t2 = runWriterT $ fst <$> execStateT (sub orig_t1 orig_t2) (mempty, mempty) where- subRet (Scalar (TypeVar _ _ v _)) rt =+ subRet (Scalar (TypeVar _ v _)) rt = unless (baseTag (qualLeaf v) <= maxIntrinsicTag) $ addSubst v rt subRet t1 (RetType _ t2) =@@ -818,7 +1160,7 @@ | Just t1' <- peelArray (arrayRank t1) t1, Just t2' <- peelArray (arrayRank t1) t2 = sub t1' t2'- sub (Scalar (TypeVar _ _ v _)) t =+ sub (Scalar (TypeVar _ v _)) t = unless (baseTag (qualLeaf v) <= maxIntrinsicTag) $ addSubst v $ RetType [] t@@ -830,7 +1172,7 @@ sub (Scalar Prim {}) (Scalar Prim {}) = pure () sub (Scalar (Arrow _ _ _ t1a (RetType _ t1b))) (Scalar (Arrow _ _ _ t2a t2b)) = do sub t1a t2a- subRet t1b t2b+ subRet (toStruct t1b) (second (const NoUniqueness) t2b) sub (Scalar (Sum cs1)) (Scalar (Sum cs2)) = zipWithM_ typeSubstClause (sortConstrs cs1) (sortConstrs cs2) where@@ -852,13 +1194,13 @@ d <- lift $ lift $ newVName "d" tell [TypeParamDim d loc] put (ts, M.insert i d sizes)- pure $ NamedSize $ qualName d+ pure $ sizeFromName (qualName d) mempty Just d ->- pure $ NamedSize $ qualName d- onDim (MonoAnon v) = pure $ AnySize $ Just v+ pure $ sizeFromName (qualName d) mempty+ onDim MonoAnon = pure anySize -- Perform a given substitution on the types in a pattern.-substPat :: Bool -> (PatType -> PatType) -> Pat -> Pat+substPat :: Bool -> (t -> t) -> Pat t -> Pat t substPat entry f pat = case pat of TuplePat pats loc -> TuplePat (map (substPat entry f) pats) loc RecordPat fs loc -> RecordPat (map substField fs) loc@@ -876,7 +1218,7 @@ toPolyBinding :: ValBind -> PolyBinding toPolyBinding (ValBind _ name _ (Info rettype) tparams params body _ attrs loc) =- PolyBinding mempty (name, tparams, params, rettype, body, attrs, loc)+ PolyBinding (name, tparams, params, rettype, body, attrs, loc) -- Remove all type variables and type abbreviations from a value binding. removeTypeVariables :: Bool -> ValBind -> MonoM ValBind@@ -888,9 +1230,8 @@ { mapOnExp = onExp, mapOnName = pure, mapOnStructType = pure . applySubst (`M.lookup` subs),- mapOnPatType = pure . applySubst (`M.lookup` subs),- mapOnStructRetType = pure . applySubst (`M.lookup` subs),- mapOnPatRetType = pure . applySubst (`M.lookup` subs)+ mapOnParamType = pure . applySubst (`M.lookup` subs),+ mapOnResRetType = pure . applySubst (`M.lookup` subs) } onExp = astMap mapper@@ -937,12 +1278,18 @@ tell $ Seq.singleton (name, valbind'' {valBindEntryPoint = Just $ Info entry'}) addLifted (valBindName valbind) (monoType t) (name, infer) - pure mempty {envPolyBindings = M.singleton (valBindName valbind) valbind'}+ pure+ mempty+ { envPolyBindings = M.singleton (valBindName valbind) valbind',+ envGlobalScope =+ if null (valBindParams valbind)+ then S.fromList $ retDims $ unInfo $ valBindRetType valbind+ else mempty+ } transformTypeBind :: TypeBind -> MonoM Env transformTypeBind (TypeBind name l tparams _ (Info (RetType dims t)) _ _) = do subs <- asks $ M.map substFromAbbr . envTypeBindings- noticeDims t let tbinding = TypeAbbr l tparams $ RetType dims $ applySubst (`M.lookup` subs) t pure mempty {envTypeBindings = M.singleton name tbinding}
+ src/Futhark/Internalise/ReplaceRecords.hs view
@@ -0,0 +1,146 @@+-- | Converts identifiers of record type into record patterns (and+-- similarly for tuples). This is to ensure that the closures+-- produced in lambda lifting and defunctionalisation do not carry+-- around huge records of which only a tiny part is needed.+module Futhark.Internalise.ReplaceRecords (transformProg) where++import Control.Monad.Reader+import Control.Monad.State+import Data.Map.Strict qualified as M+import Futhark.MonadFreshNames+import Language.Futhark+import Language.Futhark.Traversals++-- Mapping from record names to the variable names that contain the+-- fields. This is used because the monomorphiser also expands all+-- record patterns.+type RecordReplacements = M.Map VName RecordReplacement++type RecordReplacement = M.Map Name (VName, StructType)++newtype Env = Env+ { envRecordReplacements :: RecordReplacements+ }++-- The monomorphization monad.+newtype RecordM a+ = RecordM (ReaderT Env (State VNameSource) a)+ deriving+ ( Functor,+ Applicative,+ Monad,+ MonadReader Env+ )++instance MonadFreshNames RecordM where+ getNameSource = RecordM get+ putNameSource = RecordM . put++runRecordM :: VNameSource -> RecordM a -> (a, VNameSource)+runRecordM src (RecordM m) =+ runState (runReaderT m (Env mempty)) src++withRecordReplacements :: RecordReplacements -> RecordM a -> RecordM a+withRecordReplacements rr = local $ \env ->+ env {envRecordReplacements = rr <> envRecordReplacements env}++lookupRecordReplacement :: VName -> RecordM (Maybe RecordReplacement)+lookupRecordReplacement v = asks $ M.lookup v . envRecordReplacements++wildcard :: TypeBase Size u -> SrcLoc -> Pat (TypeBase Size u)+wildcard (Scalar (Record fs)) loc =+ RecordPat (zip (M.keys fs) $ map ((`Wildcard` loc) . Info) $ M.elems fs) loc+wildcard t loc =+ Wildcard (Info t) loc++transformPat :: Pat (TypeBase Size u) -> RecordM (Pat (TypeBase Size u), RecordReplacements)+transformPat (Id v (Info (Scalar (Record fs))) loc) = do+ let fs' = M.toList fs+ (fs_ks, fs_ts) <- fmap unzip $+ forM fs' $ \(f, ft) ->+ (,) <$> newVName (nameToString f) <*> pure ft+ pure+ ( RecordPat+ (zip (map fst fs') (zipWith3 Id fs_ks (map Info fs_ts) $ repeat loc))+ loc,+ M.singleton v $ M.fromList $ zip (map fst fs') $ zip fs_ks $ map toStruct fs_ts+ )+transformPat (Id v t loc) =+ pure (Id v t loc, mempty)+transformPat (TuplePat pats loc) = do+ (pats', rrs) <- mapAndUnzipM transformPat pats+ pure (TuplePat pats' loc, mconcat rrs)+transformPat (RecordPat fields loc) = do+ let (field_names, field_pats) = unzip fields+ (field_pats', rrs) <- mapAndUnzipM transformPat field_pats+ pure (RecordPat (zip field_names field_pats') loc, mconcat rrs)+transformPat (PatParens pat loc) = do+ (pat', rr) <- transformPat pat+ pure (PatParens pat' loc, rr)+transformPat (PatAttr attr pat loc) = do+ (pat', rr) <- transformPat pat+ pure (PatAttr attr pat' loc, rr)+transformPat (Wildcard (Info t) loc) =+ pure (wildcard t loc, mempty)+transformPat (PatAscription pat _ _) =+ transformPat pat+transformPat (PatLit e t loc) = pure (PatLit e t loc, mempty)+transformPat (PatConstr name t all_ps loc) = do+ (all_ps', rrs) <- mapAndUnzipM transformPat all_ps+ pure (PatConstr name t all_ps' loc, mconcat rrs)++transformExp :: Exp -> RecordM Exp+transformExp (Project n e t loc) = do+ maybe_fs <- case e of+ Var qn _ _ -> lookupRecordReplacement (qualLeaf qn)+ _ -> pure Nothing+ case maybe_fs of+ Just m+ | Just (v, _) <- M.lookup n m ->+ pure $ Var (qualName v) t loc+ _ -> do+ e' <- transformExp e+ pure $ Project n e' t loc+transformExp e@(Var fname _ loc) = do+ maybe_fs <- lookupRecordReplacement $ qualLeaf fname+ case maybe_fs of+ Just fs -> do+ let toField (f, (f_v, f_t)) = do+ let f_v' = Var (qualName f_v) (Info f_t) loc+ pure $ RecordFieldExplicit f f_v' loc+ RecordLit <$> mapM toField (M.toList fs) <*> pure loc+ Nothing ->+ pure e+transformExp (AppExp (LetPat sizes pat e body loc) res) = do+ e' <- transformExp e+ (pat', rr) <- transformPat pat+ body' <- withRecordReplacements rr $ transformExp body+ pure $ AppExp (LetPat sizes pat' e' body' loc) res+transformExp (AppExp (LetFun fname (tparams, params, retdecl, Info ret, funbody) letbody loc) res) = do+ (params', rr) <- mapAndUnzipM transformPat params+ funbody' <- withRecordReplacements (mconcat rr) $ transformExp funbody+ letbody' <- transformExp letbody+ pure $ AppExp (LetFun fname (tparams, params', retdecl, Info ret, funbody') letbody' loc) res+transformExp (Lambda params e decl tp loc) = do+ (params', rrs) <- mapAndUnzipM transformPat params+ Lambda params'+ <$> withRecordReplacements (mconcat rrs) (transformExp e)+ <*> pure decl+ <*> pure tp+ <*> pure loc+transformExp e = astMap m e+ where+ m = identityMapper {mapOnExp = transformExp}++onValBind :: ValBind -> RecordM ValBind+onValBind vb = do+ (params', rrs) <- mapAndUnzipM transformPat $ valBindParams vb+ e' <- withRecordReplacements (mconcat rrs) $ transformExp $ valBindBody vb+ pure $ vb {valBindBody = e', valBindParams = params'}++-- | Monomorphise a list of top-level declarations. A module-free input program+-- is expected, so only value declarations and type declaration are accepted.+transformProg :: MonadFreshNames m => [ValBind] -> m [ValBind]+transformProg vbs =+ modifyNameSource $ \namesrc ->+ runRecordM namesrc $ mapM onValBind vbs
src/Futhark/Internalise/TypesValues.hs view
@@ -1,6 +1,7 @@ module Futhark.Internalise.TypesValues ( -- * Internalising types internaliseReturnType,+ internaliseCoerceType, internaliseLambdaReturnType, internaliseEntryReturnType, internaliseType,@@ -9,19 +10,29 @@ internalisePrimType, internalisedTypeSize, internaliseSumType,+ Tree, -- * Internalising values internalisePrimValue,++ -- * For internal testing+ inferAliases,+ internaliseConstructors, ) where +import Control.Monad.Free (Free (..)) import Control.Monad.State import Data.Bitraversable (bitraverse)+import Data.Foldable (toList) import Data.List (delete, find, foldl')+import Data.List qualified as L import Data.Map.Strict qualified as M import Data.Maybe-import Futhark.IR.SOACS as I+import Futhark.IR.SOACS hiding (Free)+import Futhark.IR.SOACS qualified as I import Futhark.Internalise.Monad+import Futhark.Util (chunkLike) import Language.Futhark qualified as E internaliseUniqueness :: E.Uniqueness -> I.Uniqueness@@ -41,11 +52,11 @@ runInternaliseTypeM' exts (InternaliseTypeM m) = evalState m $ TypeState (length exts) internaliseParamTypes ::- [E.TypeBase E.Size ()] ->- InternaliseM [[I.TypeBase Shape Uniqueness]]+ [E.ParamType] ->+ InternaliseM [[Tree (I.TypeBase Shape Uniqueness)]] internaliseParamTypes ts =- mapM (mapM mkAccCerts) . runInternaliseTypeM $- mapM (fmap (map onType) . internaliseTypeM mempty) ts+ mapM (mapM (mapM mkAccCerts)) . runInternaliseTypeM $+ mapM (fmap (map (fmap onType)) . internaliseTypeM mempty . E.paramToRes) ts where onType = fromMaybe bad . hasStaticShape bad = error $ "internaliseParamTypes: " ++ prettyString ts@@ -53,10 +64,13 @@ -- We need to fix up the arrays for any Acc return values or loop -- parameters. We look at the concrete types for this, since the Acc -- parameter name in the second list will just be something we made up.-fixupKnownTypes :: [TypeBase shape1 u1] -> [TypeBase shape2 u2] -> [TypeBase shape2 u2]+fixupKnownTypes ::+ [TypeBase shape1 u1] ->+ [(TypeBase shape2 u2, b)] ->+ [(TypeBase shape2 u2, b)] fixupKnownTypes = zipWith fixup where- fixup (Acc acc ispace ts _) (Acc _ _ _ u2) = Acc acc ispace ts u2+ fixup (Acc acc ispace ts _) (Acc _ _ _ u2, b) = (Acc acc ispace ts u2, b) fixup _ t = t -- Generate proper certificates for the placeholder accumulator@@ -74,45 +88,120 @@ mkAccCerts t = pure t internaliseLoopParamType ::- E.TypeBase E.Size () ->+ E.ParamType -> [TypeBase shape u] -> InternaliseM [I.TypeBase Shape Uniqueness] internaliseLoopParamType et ts =- fixupKnownTypes ts . concat <$> internaliseParamTypes [et]+ map fst . fixupKnownTypes ts . map (,()) . concatMap (concatMap toList)+ <$> internaliseParamTypes [et] +-- Tag every sublist with its offset in corresponding flattened list.+withOffsets :: Foldable a => [a b] -> [(a b, Int)]+withOffsets xs = zip xs (scanl (+) 0 $ map length xs)++numberFrom :: Int -> Tree a -> Tree (a, Int)+numberFrom o = flip evalState o . f+ where+ f (Pure x) = state $ \i -> (Pure (x, i), i + 1)+ f (Free xs) = Free <$> traverse f xs++numberTrees :: [Tree a] -> [Tree (a, Int)]+numberTrees = map (uncurry $ flip numberFrom) . withOffsets++nonuniqueArray :: TypeBase shape Uniqueness -> Bool+nonuniqueArray t@Array {} = not $ unique t+nonuniqueArray _ = False++matchTrees :: Tree a -> Tree b -> Maybe (Tree (a, b))+matchTrees (Pure a) (Pure b) = Just $ Pure (a, b)+matchTrees (Free as) (Free bs)+ | length as == length bs =+ Free <$> zipWithM matchTrees as bs+matchTrees _ _ = Nothing++subtreesMatching :: Tree a -> Tree b -> [Tree (a, b)]+subtreesMatching as bs =+ case matchTrees as bs of+ Just m -> [m]+ Nothing -> case bs of+ Pure _ -> []+ Free bs' -> foldMap (subtreesMatching as) bs'++-- See Note [Alias Inference].+inferAliases ::+ [Tree (I.TypeBase Shape Uniqueness)] ->+ [Tree (I.TypeBase ExtShape Uniqueness)] ->+ [[(I.TypeBase ExtShape Uniqueness, RetAls)]]+inferAliases all_param_ts all_res_ts =+ map onRes all_res_ts+ where+ all_res_ts' = numberTrees all_res_ts+ all_param_ts' = numberTrees all_param_ts+ aliasable_param_ts = filter (all $ nonuniqueArray . fst) all_param_ts'+ aliasable_res_ts = filter (all $ nonuniqueArray . fst) all_res_ts'+ onRes (Pure res_t) =+ -- Necessarily a non-array.+ [(res_t, RetAls mempty mempty)]+ onRes (Free res_ts) =+ [ if nonuniqueArray res_t+ then (res_t, RetAls pals rals)+ else (res_t, mempty)+ | (res_t, pals, rals) <- zip3 (toList (Free res_ts)) palss ralss+ ]+ where+ reorder [] = replicate (length (Free res_ts)) []+ reorder xs = L.transpose xs+ infer ts =+ reorder . map (toList . fmap (snd . snd)) $+ foldMap (subtreesMatching (Free res_ts)) ts+ palss = infer aliasable_param_ts+ ralss = infer aliasable_res_ts+ internaliseReturnType ::- E.StructRetType ->+ [Tree (I.TypeBase Shape Uniqueness)] ->+ E.ResRetType -> [TypeBase shape u] ->- [I.TypeBase ExtShape Uniqueness]-internaliseReturnType (E.RetType dims et) ts =- fixupKnownTypes ts $ runInternaliseTypeM' dims (internaliseTypeM exts et)+ [(I.TypeBase ExtShape Uniqueness, RetAls)]+internaliseReturnType paramts (E.RetType dims et) ts =+ fixupKnownTypes ts . concat . inferAliases paramts $+ runInternaliseTypeM' dims (internaliseTypeM exts et) where exts = M.fromList $ zip dims [0 ..] -internaliseLambdaReturnType ::- E.TypeBase E.Size () ->- [TypeBase shape u] ->- InternaliseM [I.TypeBase Shape NoUniqueness]-internaliseLambdaReturnType et ts =- map fromDecl <$> internaliseLoopParamType et ts- -- | As 'internaliseReturnType', but returns components of a top-level -- tuple type piecemeal. internaliseEntryReturnType ::- E.StructRetType ->- [[I.TypeBase ExtShape Uniqueness]]-internaliseEntryReturnType (E.RetType dims et) =- runInternaliseTypeM' dims . mapM (internaliseTypeM exts) $- case E.isTupleRecord et of- Just ets | not $ null ets -> ets- _ -> [et]+ [Tree (I.TypeBase Shape Uniqueness)] ->+ E.ResRetType ->+ [[(I.TypeBase ExtShape Uniqueness, RetAls)]]+internaliseEntryReturnType paramts (E.RetType dims et) =+ let et' = runInternaliseTypeM' dims . mapM (internaliseTypeM exts) $+ case E.isTupleRecord et of+ Just ets | not $ null ets -> ets+ _ -> [et]+ in map concat $ chunkLike et' $ inferAliases paramts $ concat et' where exts = M.fromList $ zip dims [0 ..] +internaliseCoerceType ::+ E.StructType ->+ [TypeBase shape u] ->+ [I.TypeBase ExtShape Uniqueness]+internaliseCoerceType et ts =+ map fst $ internaliseReturnType [] (E.RetType [] $ E.toRes E.Nonunique et) ts++internaliseLambdaReturnType ::+ E.ResType ->+ [TypeBase shape u] ->+ InternaliseM [I.TypeBase Shape NoUniqueness]+internaliseLambdaReturnType et ts =+ map fromDecl <$> internaliseLoopParamType (E.resToParam et) ts+ internaliseType ::- E.TypeBase E.Size () ->- [I.TypeBase I.ExtShape Uniqueness]-internaliseType = runInternaliseTypeM . internaliseTypeM mempty+ E.TypeBase E.Size NoUniqueness ->+ [Tree (I.TypeBase I.ExtShape Uniqueness)]+internaliseType =+ runInternaliseTypeM . internaliseTypeM mempty . E.toRes E.Nonunique newId :: InternaliseTypeM Int newId = do@@ -126,39 +215,54 @@ InternaliseTypeM ExtSize internaliseDim exts d = case d of- E.AnySize _ -> Ext <$> newId- E.ConstSize n -> pure $ Free $ intConst I.Int64 $ toInteger n- E.NamedSize name -> pure $ namedDim name+ e | e == E.anySize -> Ext <$> newId+ (E.IntLit n _ _) -> pure $ I.Free $ intConst I.Int64 n+ (E.Var name _ _) -> pure $ namedDim name+ e -> error $ "Unexpected size expression: " ++ prettyString e where namedDim (E.QualName _ name) | Just x <- name `M.lookup` exts = I.Ext x | otherwise = I.Free $ I.Var name +-- | A tree is just an instantiation of the free monad with a list+-- monad.+--+-- The important thing is that we use it to represent the original+-- structure of arrayss, as this matters for aliasing. Each 'Free'+-- constructor corresponds to an array dimension. Only non-arrays+-- have a 'Pure' at the top level. See Note [Alias Inference].+type Tree = Free []+ internaliseTypeM :: M.Map VName Int ->- E.StructType ->- InternaliseTypeM [I.TypeBase ExtShape Uniqueness]+ E.ResType ->+ InternaliseTypeM [Tree (I.TypeBase ExtShape Uniqueness)] internaliseTypeM exts orig_t = case orig_t of- E.Array _ u shape et -> do+ E.Array u shape et -> do dims <- internaliseShape shape- ets <- internaliseTypeM exts $ E.Scalar et- pure [I.arrayOf et' (Shape dims) $ internaliseUniqueness u | et' <- ets]+ ets <- internaliseTypeM exts $ E.toRes E.Nonunique $ E.Scalar et+ let f et' = I.arrayOf et' (Shape dims) $ internaliseUniqueness u+ pure [array $ map (fmap f) ets] E.Scalar (E.Prim bt) ->- pure [I.Prim $ internalisePrimType bt]+ pure [Pure $ I.Prim $ internalisePrimType bt] E.Scalar (E.Record ets)- -- XXX: we map empty records to units, because otherwise- -- arrays of unit will lose their sizes.- | null ets -> pure [I.Prim I.Unit]+ -- We map empty records to units, because otherwise arrays of+ -- unit will lose their sizes.+ | null ets -> pure [Pure $ I.Prim I.Unit] | otherwise -> concat <$> mapM (internaliseTypeM exts . snd) (E.sortFields ets)- E.Scalar (E.TypeVar _ u tn [E.TypeArgType arr_t _])+ E.Scalar (E.TypeVar u tn [E.TypeArgType arr_t]) | baseTag (E.qualLeaf tn) <= E.maxIntrinsicTag, baseString (E.qualLeaf tn) == "acc" -> do- ts <- map (fromDecl . onAccType) <$> internaliseTypeM exts arr_t+ ts <-+ foldMap (toList . fmap (fromDecl . onAccType))+ <$> internaliseTypeM exts (E.toRes Nonunique arr_t) let acc_param = VName "PLACEHOLDER" 0 -- See mkAccCerts.- acc_t = Acc acc_param (Shape [arraysSize 0 ts]) (map rowType ts) $ internaliseUniqueness u- pure [acc_t]+ acc_shape = Shape [arraysSize 0 ts]+ u' = internaliseUniqueness u+ acc_t = Acc acc_param acc_shape (map rowType ts) u'+ pure [Pure acc_t] E.Scalar E.TypeVar {} -> error $ "internaliseTypeM: cannot handle type variable: " ++ prettyString orig_t E.Scalar E.Arrow {} ->@@ -167,16 +271,19 @@ (ts, _) <- internaliseConstructors <$> traverse (fmap concat . mapM (internaliseTypeM exts)) cs- pure $ I.Prim (I.IntType I.Int8) : ts+ pure $ Pure (I.Prim (I.IntType I.Int8)) : ts where internaliseShape = mapM (internaliseDim exts) . E.shapeDims+ array [Free ts] = Free ts+ array ts = Free ts onAccType = fromMaybe bad . hasStaticShape bad = error $ "internaliseTypeM Acc: " ++ prettyString orig_t +-- | Only exposed for testing purposes. internaliseConstructors ::- M.Map Name [I.TypeBase ExtShape Uniqueness] ->- ( [I.TypeBase ExtShape Uniqueness],+ M.Map Name [Tree (I.TypeBase ExtShape Uniqueness)] ->+ ( [Tree (I.TypeBase ExtShape Uniqueness)], M.Map Name (Int, [Int]) ) internaliseConstructors cs =@@ -184,18 +291,19 @@ where onConstructor (ts, mapping) ((c, c_ts), i) = let (_, js, new_ts) =- foldl' f (zip (map fromDecl ts) [0 ..], mempty, mempty) c_ts+ foldl' f (withOffsets (map (fmap fromDecl) ts), mempty, mempty) c_ts in (ts ++ new_ts, M.insert c (i, js) mapping) where+ size = sum . map length f (ts', js, new_ts) t- | Just (_, j) <- find ((== fromDecl t) . fst) ts' =- ( delete (fromDecl t, j) ts',- js ++ [j],+ | Just (_, j) <- find ((== fmap fromDecl t) . fst) ts' =+ ( delete (fmap fromDecl t, j) ts',+ js ++ take (length t) [j ..], new_ts ) | otherwise = ( ts',- js ++ [length ts + length new_ts],+ js ++ take (length t) [size ts + size new_ts ..], new_ts ++ [t] ) @@ -206,17 +314,17 @@ M.Map Name (Int, [Int]) ) internaliseSumType cs =- bitraverse (mapM mkAccCerts) pure . runInternaliseTypeM $+ bitraverse (mapM mkAccCerts . foldMap toList) pure . runInternaliseTypeM $ internaliseConstructors- <$> traverse (fmap concat . mapM (internaliseTypeM mempty)) cs+ <$> traverse (fmap concat . mapM (internaliseTypeM mempty . E.toRes E.Nonunique)) cs -- | How many core language values are needed to represent one source -- language value of the given type? internalisedTypeSize :: E.TypeBase E.Size als -> Int -- A few special cases for performance. internalisedTypeSize (E.Scalar (E.Prim _)) = 1-internalisedTypeSize (E.Array _ _ _ (E.Prim _)) = 1-internalisedTypeSize t = length $ internaliseType (t `E.setAliases` ())+internalisedTypeSize (E.Array _ _ (E.Prim _)) = 1+internalisedTypeSize t = sum $ map length $ internaliseType $ E.toStruct t -- | Convert an external primitive to an internal primitive. internalisePrimType :: E.PrimType -> I.PrimType@@ -231,3 +339,50 @@ internalisePrimValue (E.UnsignedValue v) = I.IntValue v internalisePrimValue (E.FloatValue v) = I.FloatValue v internalisePrimValue (E.BoolValue b) = I.BoolValue b++-- Note [Alias Inference]+--+-- The core language requires us to precisely indicate the aliasing of+-- function results (the RetAls type). This is a problem when coming+-- from the source language, where it is implicit: a non-unique+-- function return value aliases every function argument. The problem+-- now occurs because the core language uses a different value+-- representation than the source language - in particular, we do not+-- have arrays of tuples. E.g. @([]i32,[]i32)@ and @[](i32,i32)@ both+-- have the same core representation, but their implications for+-- aliasing are different.+--+--+-- To understand why this is a problem, consider a source program+--+-- def id (x: [](i32,i32)) = x+--+-- def f n =+-- let x = replicate n (0,0)+-- let x' = id x+-- let x'' = x' with [0] = (1,1)+-- in x''+--+-- With the core language value representation, it will be this:+--+-- def id (x1: []i32) (x2: []i32) = (x1,x2)+--+-- def f n =+-- let x1 = replicate n 0+-- let x2 = replicate n 0+-- let (x1', x2') = id x1 x2+-- let x1'' = x1' with [0] = 1+-- let x2'' = x2' with [0] = 1+-- in (x1'', x2'')+--+-- The results of 'id' alias *both* of the arguments, so x1' aliases+-- x1 and x2, and x2' also aliases x1 and x2. This means that the+-- first with-expression will consume all of x1/x2/x1'/x2', and then+-- the second with-expression is a type error, as it references a+-- consumed variable.+--+-- Our solution is to deduce the possible aliasing such that+-- components that originally constituted the same array-of-tuples are+-- not aliased. The main complexity is that we have to keep+-- information on the original (source) type structure around for a+-- while. This is done with the Tree type.
src/Futhark/LSP/Compile.hs view
@@ -16,13 +16,13 @@ import Futhark.LSP.State (State (..), emptyState, updateStaleContent, updateStaleMapping) import Futhark.LSP.Tool (computeMapping) import Language.Futhark.Warnings (listWarnings)-import Language.LSP.Server (LspT, flushDiagnosticsBySource, getVirtualFile, getVirtualFiles)-import Language.LSP.Types+import Language.LSP.Protocol.Types ( filePathToUri, fromNormalizedFilePath, toNormalizedUri, uriToNormalizedFilePath, )+import Language.LSP.Server (LspT, flushDiagnosticsBySource, getVirtualFile, getVirtualFiles) import Language.LSP.VFS (VFS, vfsMap, virtualFileText) -- | Try to take state from IORef, if it's empty, try to compile.
src/Futhark/LSP/Diagnostic.hs view
@@ -19,19 +19,22 @@ import Futhark.Util.Loc (Loc (..), SrcLoc, locOf) import Futhark.Util.Pretty (Doc, docText) import Language.LSP.Diagnostics (partitionBySource)+import Language.LSP.Protocol.Lens (HasVersion (version))+import Language.LSP.Protocol.Types import Language.LSP.Server (LspT, getVersionedTextDoc, publishDiagnostics)-import Language.LSP.Types- ( Diagnostic (Diagnostic),- DiagnosticSeverity (DsError, DsWarning),- Range,- TextDocumentIdentifier (TextDocumentIdentifier),- Uri,- toNormalizedUri,- )-import Language.LSP.Types.Lens (HasVersion (version)) mkDiagnostic :: Range -> DiagnosticSeverity -> T.Text -> Diagnostic-mkDiagnostic range severity msg = Diagnostic range (Just severity) Nothing diagnosticSource msg Nothing Nothing+mkDiagnostic range severity msg =+ Diagnostic+ range+ (Just severity)+ Nothing+ Nothing+ diagnosticSource+ msg+ Nothing+ Nothing+ Nothing -- | Publish diagnostics from a Uri to Diagnostics mapping. publish :: [(Uri, [Diagnostic])] -> LspT () IO ()@@ -39,7 +42,11 @@ doc <- getVersionedTextDoc $ TextDocumentIdentifier uri logStringStderr <& ("Publishing diagnostics for " ++ show uri ++ " Version: " ++ show (doc ^. version))- publishDiagnostics maxDiagnostic (toNormalizedUri uri) (doc ^. version) (partitionBySource diags)+ publishDiagnostics+ maxDiagnostic+ (toNormalizedUri uri)+ (Just $ doc ^. version)+ (partitionBySource diags) -- | Send warning diagnostics to the client. publishWarningDiagnostics :: [(SrcLoc, Doc a)] -> LspT () IO ()@@ -47,26 +54,40 @@ publish $ M.assocs $ M.unionsWith (++) $ map onWarn warnings where onWarn (srcloc, msg) =- let diag = mkDiagnostic (rangeFromSrcLoc srcloc) DsWarning (docText msg)- in case locOf srcloc of- NoLoc -> mempty- Loc pos _ -> M.singleton (posToUri pos) [diag]+ case locOf srcloc of+ NoLoc -> mempty+ Loc pos _ ->+ M.singleton+ (posToUri pos)+ [ mkDiagnostic+ (rangeFromSrcLoc srcloc)+ DiagnosticSeverity_Warning+ (docText msg)+ ] -- | Send error diagnostics to the client. publishErrorDiagnostics :: NE.NonEmpty ProgError -> LspT () IO () publishErrorDiagnostics errors = publish $ M.assocs $ M.unionsWith (++) $ map onDiag $ NE.toList errors where- onDiag (ProgError loc msg) =- let diag = mkDiagnostic (rangeFromLoc loc) DsError (docText msg)- in case loc of- NoLoc -> mempty- Loc pos _ -> M.singleton (posToUri pos) [diag]- onDiag (ProgWarning loc msg) =- let diag = mkDiagnostic (rangeFromLoc loc) DsError (docText msg)- in case loc of- NoLoc -> mempty- Loc pos _ -> M.singleton (posToUri pos) [diag]+ onDiag (ProgError NoLoc _) = mempty+ onDiag (ProgError loc@(Loc pos _) msg) =+ M.singleton+ (posToUri pos)+ [ mkDiagnostic+ (rangeFromLoc loc)+ DiagnosticSeverity_Error+ (docText msg)+ ]+ onDiag (ProgWarning NoLoc _) = mempty+ onDiag (ProgWarning loc@(Loc pos _) msg) =+ M.singleton+ (posToUri pos)+ [ mkDiagnostic+ (rangeFromLoc loc)+ DiagnosticSeverity_Error+ (docText msg)+ ] -- | The maximum number of diagnostics to report. maxDiagnostic :: Int
src/Futhark/LSP/Handlers.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DataKinds #-}+ -- | The handlers exposed by the language server. module Futhark.LSP.Handlers (handlers) where @@ -5,75 +7,83 @@ import Control.Lens ((^.)) import Data.Aeson.Types (Value (Array, String)) import Data.IORef+import Data.Proxy (Proxy (..)) import Data.Vector qualified as V import Futhark.LSP.Compile (tryReCompile, tryTakeStateFromIORef) import Futhark.LSP.State (State (..)) import Futhark.LSP.Tool (findDefinitionRange, getHoverInfoFromState)+import Language.LSP.Protocol.Lens (HasUri (uri))+import Language.LSP.Protocol.Message+import Language.LSP.Protocol.Types import Language.LSP.Server (Handlers, LspM, notificationHandler, requestHandler)-import Language.LSP.Types-import Language.LSP.Types.Lens (HasUri (uri)) onInitializeHandler :: Handlers (LspM ())-onInitializeHandler = notificationHandler SInitialized $ \_msg ->+onInitializeHandler = notificationHandler SMethod_Initialized $ \_msg -> logStringStderr <& "Initialized" onHoverHandler :: IORef State -> Handlers (LspM ())-onHoverHandler state_mvar = requestHandler STextDocumentHover $ \req responder -> do- let RequestMessage _ _ _ (HoverParams doc pos _workDone) = req- Position l c = pos- file_path = uriToFilePath $ doc ^. uri- logStringStderr <& ("Got hover request: " <> show (file_path, pos))- state <- tryTakeStateFromIORef state_mvar file_path- responder $ Right $ getHoverInfoFromState state file_path (fromEnum l + 1) (fromEnum c + 1)+onHoverHandler state_mvar =+ requestHandler SMethod_TextDocumentHover $ \req responder -> do+ let TRequestMessage _ _ _ (HoverParams doc pos _workDone) = req+ Position l c = pos+ file_path = uriToFilePath $ doc ^. uri+ logStringStderr <& ("Got hover request: " <> show (file_path, pos))+ state <- tryTakeStateFromIORef state_mvar file_path+ responder $ Right $ maybe (InR Null) InL $ getHoverInfoFromState state file_path (fromEnum l + 1) (fromEnum c + 1) onDocumentFocusHandler :: IORef State -> Handlers (LspM ())-onDocumentFocusHandler state_mvar = notificationHandler (SCustomMethod "custom/onFocusTextDocument") $ \msg -> do- logStringStderr <& "Got custom request: onFocusTextDocument"- let NotificationMessage _ _ (Array vector_param) = msg- String focused_uri = V.head vector_param -- only one parameter passed from the client- tryReCompile state_mvar (uriToFilePath (Uri focused_uri))+onDocumentFocusHandler state_mvar =+ notificationHandler (SMethod_CustomMethod (Proxy @"custom/onFocusTextDocument")) $ \msg -> do+ logStringStderr <& "Got custom request: onFocusTextDocument"+ let TNotificationMessage _ _ (Array vector_param) = msg+ String focused_uri = V.head vector_param -- only one parameter passed from the client+ tryReCompile state_mvar (uriToFilePath (Uri focused_uri)) goToDefinitionHandler :: IORef State -> Handlers (LspM ())-goToDefinitionHandler state_mvar = requestHandler STextDocumentDefinition $ \req responder -> do- let RequestMessage _ _ _ (DefinitionParams doc pos _workDone _partial) = req- Position l c = pos- file_path = uriToFilePath $ doc ^. uri- logStringStderr <& ("Got goto definition: " <> show (file_path, pos))- state <- tryTakeStateFromIORef state_mvar file_path- case findDefinitionRange state file_path (fromEnum l + 1) (fromEnum c + 1) of- Nothing -> responder $ Right $ InR $ InL $ List []- Just loc -> responder $ Right $ InL loc+goToDefinitionHandler state_mvar =+ requestHandler SMethod_TextDocumentDefinition $ \req responder -> do+ let TRequestMessage _ _ _ (DefinitionParams doc pos _workDone _partial) = req+ Position l c = pos+ file_path = uriToFilePath $ doc ^. uri+ logStringStderr <& ("Got goto definition: " <> show (file_path, pos))+ state <- tryTakeStateFromIORef state_mvar file_path+ case findDefinitionRange state file_path (fromEnum l + 1) (fromEnum c + 1) of+ Nothing -> responder $ Right $ InR $ InR Null+ Just loc -> responder $ Right $ InL $ Definition $ InL loc onDocumentSaveHandler :: IORef State -> Handlers (LspM ())-onDocumentSaveHandler state_mvar = notificationHandler STextDocumentDidSave $ \msg -> do- let NotificationMessage _ _ (DidSaveTextDocumentParams doc _text) = msg- file_path = uriToFilePath $ doc ^. uri- logStringStderr <& ("Saved document: " ++ show doc)- tryReCompile state_mvar file_path+onDocumentSaveHandler state_mvar =+ notificationHandler SMethod_TextDocumentDidSave $ \msg -> do+ let TNotificationMessage _ _ (DidSaveTextDocumentParams doc _text) = msg+ file_path = uriToFilePath $ doc ^. uri+ logStringStderr <& ("Saved document: " ++ show doc)+ tryReCompile state_mvar file_path onDocumentChangeHandler :: IORef State -> Handlers (LspM ())-onDocumentChangeHandler state_mvar = notificationHandler STextDocumentDidChange $ \msg -> do- let NotificationMessage _ _ (DidChangeTextDocumentParams doc _content) = msg- file_path = uriToFilePath $ doc ^. uri- tryReCompile state_mvar file_path+onDocumentChangeHandler state_mvar =+ notificationHandler SMethod_TextDocumentDidChange $ \msg -> do+ let TNotificationMessage _ _ (DidChangeTextDocumentParams doc _content) = msg+ file_path = uriToFilePath $ doc ^. uri+ tryReCompile state_mvar file_path onDocumentOpenHandler :: IORef State -> Handlers (LspM ())-onDocumentOpenHandler state_mvar = notificationHandler STextDocumentDidOpen $ \msg -> do- let NotificationMessage _ _ (DidOpenTextDocumentParams doc) = msg- file_path = uriToFilePath $ doc ^. uri- logStringStderr <& ("Opened document: " ++ show (doc ^. uri))- tryReCompile state_mvar file_path+onDocumentOpenHandler state_mvar =+ notificationHandler SMethod_TextDocumentDidOpen $ \msg -> do+ let TNotificationMessage _ _ (DidOpenTextDocumentParams doc) = msg+ file_path = uriToFilePath $ doc ^. uri+ logStringStderr <& ("Opened document: " ++ show (doc ^. uri))+ tryReCompile state_mvar file_path onDocumentCloseHandler :: Handlers (LspM ())-onDocumentCloseHandler = notificationHandler STextDocumentDidClose $ \_msg ->- logStringStderr <& "Closed document"+onDocumentCloseHandler =+ notificationHandler SMethod_TextDocumentDidClose $ \_msg ->+ logStringStderr <& "Closed document" -- Sent by Eglot when first connecting - not sure when else it might -- be sent. onWorkspaceDidChangeConfiguration :: IORef State -> Handlers (LspM ()) onWorkspaceDidChangeConfiguration _state_mvar =- notificationHandler SWorkspaceDidChangeConfiguration $ \msg -> do- let NotificationMessage _ _ (DidChangeConfigurationParams _settings) = msg+ notificationHandler SMethod_WorkspaceDidChangeConfiguration $ \_ -> logStringStderr <& "WorkspaceDidChangeConfiguration" -- | Given an 'IORef' tracking the state, produce a set of handlers.
src/Futhark/LSP/Tool.hs view
@@ -28,8 +28,8 @@ atPos, boundLoc, )+import Language.LSP.Protocol.Types import Language.LSP.Server (LspM, getVirtualFile)-import Language.LSP.Types import Language.LSP.VFS (VirtualFile, virtualFileText, virtualFileVersion) -- | Retrieve hover info for the definition referenced at the given@@ -43,8 +43,8 @@ BoundModule {} -> "module" BoundModuleType {} -> "module type" BoundType {} -> "type"- ms = HoverContents $ MarkupContent MkPlainText msg- Just $ Hover ms (Just (rangeFromLoc loc))+ ms = MarkupContent MarkupKind_PlainText msg+ Just $ Hover (InL ms) (Just (rangeFromLoc loc)) getHoverInfoFromState _ _ _ _ = Nothing -- | Find the location of the definition referenced at the given file
src/Futhark/Optimise/ArrayShortCircuiting.hs view
@@ -13,6 +13,7 @@ import Control.Monad import Control.Monad.Reader import Data.Function ((&))+import Data.List qualified as L import Data.Map qualified as M import Data.Maybe (fromMaybe) import Futhark.Analysis.Alias qualified as AnlAls@@ -117,10 +118,14 @@ (Mem rep inner, LetDec rep ~ LetDecMem) => Stm rep -> UpdateM (inner rep) (Stm rep)-replaceInStm (Let (Pat elems) d e) = do+replaceInStm (Let (Pat elems) (StmAux c a d) e) = do elems' <- mapM replaceInPatElem elems e' <- replaceInExp elems' e- pure $ Let (Pat elems') d e'+ entries <- asks (M.elems . envCoalesceTab)+ let c' = case filter (\entry -> (map patElemName elems `L.intersect` M.keys (vartab entry)) /= []) entries of+ [] -> c+ entries' -> c <> foldMap certs entries'+ pure $ Let (Pat elems') (StmAux c' a d) e' where replaceInPatElem :: PatElem LetDecMem -> UpdateM inner (PatElem LetDecMem) replaceInPatElem p@(PatElem vname (MemArray _ _ u _)) =
src/Futhark/Optimise/ArrayShortCircuiting/ArrayCoalescing.hs view
@@ -58,12 +58,20 @@ type ScalarTableM rep a = Reader (ComputeScalarTableOnOp rep) a data ShortCircuitReader rep = ShortCircuitReader- { onOp :: LUTabFun -> Pat (VarAliases, LetDecMem) -> Op (Aliases rep) -> TopdownEnv rep -> BotUpEnv -> ShortCircuitM rep BotUpEnv,+ { onOp ::+ LUTabFun ->+ Pat (VarAliases, LetDecMem) ->+ Certs ->+ Op (Aliases rep) ->+ TopdownEnv rep ->+ BotUpEnv ->+ ShortCircuitM rep BotUpEnv, ssPointFromOp :: LUTabFun -> TopdownEnv rep -> ScopeTab rep -> Pat (VarAliases, LetDecMem) ->+ Certs -> Op (Aliases rep) -> Maybe [SSPointInfo] }@@ -140,7 +148,8 @@ ComputeScalarTableOnOp rep -> Prog (Aliases rep) -> m (M.Map Name CoalsTab)-mkCoalsTabProg (_, lutab_prog) r computeScalarOnOp = fmap M.fromList . mapM onFun . progFuns+mkCoalsTabProg (_, lutab_prog) r computeScalarOnOp =+ fmap M.fromList . mapM onFun . progFuns where onFun fun@(FunDef _ _ fname _ fpars body) = do -- First compute last-use information@@ -171,8 +180,8 @@ -- -- Because 'SeqMem' don't have any special operation, simply return the input -- 'BotUpEnv'.-shortCircuitSeqMem :: LUTabFun -> Pat (VarAliases, LetDecMem) -> Op (Aliases SeqMem) -> TopdownEnv SeqMem -> BotUpEnv -> ShortCircuitM SeqMem BotUpEnv-shortCircuitSeqMem _ _ _ _ = pure+shortCircuitSeqMem :: LUTabFun -> Pat (VarAliases, LetDecMem) -> Certs -> Op (Aliases SeqMem) -> TopdownEnv SeqMem -> BotUpEnv -> ShortCircuitM SeqMem BotUpEnv+shortCircuitSeqMem _ _ _ _ _ = pure -- | Short-circuit handler for SegOp. shortCircuitSegOp ::@@ -180,14 +189,15 @@ (lvl -> Bool) -> LUTabFun -> Pat (VarAliases, LetDecMem) ->+ Certs -> SegOp lvl (Aliases rep) -> TopdownEnv rep -> BotUpEnv -> ShortCircuitM rep BotUpEnv-shortCircuitSegOp lvlOK lutab pat (SegMap lvl space _ kernel_body) td_env bu_env =+shortCircuitSegOp lvlOK lutab pat pat_certs (SegMap lvl space _ kernel_body) td_env bu_env = -- No special handling necessary for 'SegMap'. Just call the helper-function.- shortCircuitSegOpHelper 0 lvlOK lvl lutab pat space kernel_body td_env bu_env-shortCircuitSegOp lvlOK lutab pat (SegRed lvl space binops _ kernel_body) td_env bu_env =+ shortCircuitSegOpHelper 0 lvlOK lvl lutab pat pat_certs space kernel_body td_env bu_env+shortCircuitSegOp lvlOK lutab pat pat_certs (SegRed lvl space binops _ kernel_body) td_env bu_env = -- When handling 'SegRed', we we first invalidate all active coalesce-entries -- where any of the variables in 'vartab' are also free in the list of -- 'SegBinOp'. In other words, anything that is used as part of the reduction@@ -197,26 +207,26 @@ foldl markFailedCoal (activeCoals bu_env, inhibit bu_env) $ M.keys to_fail bu_env' = bu_env {activeCoals = active, inhibit = inh} num_reds = length red_ts- in shortCircuitSegOpHelper num_reds lvlOK lvl lutab pat space kernel_body td_env bu_env'+ in shortCircuitSegOpHelper num_reds lvlOK lvl lutab pat pat_certs space kernel_body td_env bu_env' where segment_dims = init $ segSpaceDims space red_ts = do op <- binops let shp = Shape segment_dims <> segBinOpShape op map (`arrayOfShape` shp) (lambdaReturnType $ segBinOpLambda op)-shortCircuitSegOp lvlOK lutab pat (SegScan lvl space binops _ kernel_body) td_env bu_env =+shortCircuitSegOp lvlOK lutab pat pat_certs (SegScan lvl space binops _ kernel_body) td_env bu_env = -- Like in the handling of 'SegRed', we do not want to coalesce anything that -- is used in the 'SegBinOp' let to_fail = M.filter (\entry -> namesFromList (M.keys $ vartab entry) `namesIntersect` foldMap (freeIn . segBinOpLambda) binops) $ activeCoals bu_env (active, inh) = foldl markFailedCoal (activeCoals bu_env, inhibit bu_env) $ M.keys to_fail bu_env' = bu_env {activeCoals = active, inhibit = inh}- in shortCircuitSegOpHelper 0 lvlOK lvl lutab pat space kernel_body td_env bu_env'-shortCircuitSegOp lvlOK lutab pat (SegHist lvl space histops _ kernel_body) td_env bu_env = do+ in shortCircuitSegOpHelper 0 lvlOK lvl lutab pat pat_certs space kernel_body td_env bu_env'+shortCircuitSegOp lvlOK lutab pat pat_certs (SegHist lvl space histops _ kernel_body) td_env bu_env = do -- Need to take zipped patterns and histDest (flattened) and insert transitive coalesces let to_fail = M.filter (\entry -> namesFromList (M.keys $ vartab entry) `namesIntersect` foldMap (freeIn . histOp) histops) $ activeCoals bu_env (active, inh) = foldl markFailedCoal (activeCoals bu_env, inhibit bu_env) $ M.keys to_fail bu_env' = bu_env {activeCoals = active, inhibit = inh}- bu_env'' <- shortCircuitSegOpHelper 0 lvlOK lvl lutab pat space kernel_body td_env bu_env'+ bu_env'' <- shortCircuitSegOpHelper 0 lvlOK lvl lutab pat pat_certs space kernel_body td_env bu_env' pure $ foldl insertHistCoals bu_env'' $ zip (patElems pat) $@@ -245,14 +255,15 @@ shortCircuitGPUMem :: LUTabFun -> Pat (VarAliases, LetDecMem) ->+ Certs -> Op (Aliases GPUMem) -> TopdownEnv GPUMem -> BotUpEnv -> ShortCircuitM GPUMem BotUpEnv-shortCircuitGPUMem _ _ (Alloc _ _) _ bu_env = pure bu_env-shortCircuitGPUMem lutab pat (Inner (GPU.SegOp op)) td_env bu_env =- shortCircuitSegOp isSegThread lutab pat op td_env bu_env-shortCircuitGPUMem lutab pat (Inner (GPU.GPUBody _ body)) td_env bu_env = do+shortCircuitGPUMem _ _ _ (Alloc _ _) _ bu_env = pure bu_env+shortCircuitGPUMem lutab pat certs (Inner (GPU.SegOp op)) td_env bu_env =+ shortCircuitSegOp isSegThread lutab pat certs op td_env bu_env+shortCircuitGPUMem lutab pat certs (Inner (GPU.GPUBody _ body)) td_env bu_env = do fresh1 <- newNameFromString "gpubody" fresh2 <- newNameFromString "gpubody" shortCircuitSegOpHelper@@ -267,27 +278,29 @@ ) lutab pat+ certs (SegSpace fresh1 [(fresh2, Constant $ IntValue $ Int64Value 1)]) (bodyToKernelBody body) td_env bu_env-shortCircuitGPUMem _ _ (Inner (GPU.SizeOp _)) _ bu_env = pure bu_env-shortCircuitGPUMem _ _ (Inner (GPU.OtherOp NoOp)) _ bu_env = pure bu_env+shortCircuitGPUMem _ _ _ (Inner (GPU.SizeOp _)) _ bu_env = pure bu_env+shortCircuitGPUMem _ _ _ (Inner (GPU.OtherOp NoOp)) _ bu_env = pure bu_env shortCircuitMCMem :: LUTabFun -> Pat (VarAliases, LetDecMem) ->+ Certs -> Op (Aliases MCMem) -> TopdownEnv MCMem -> BotUpEnv -> ShortCircuitM MCMem BotUpEnv-shortCircuitMCMem _ _ (Alloc _ _) _ bu_env = pure bu_env-shortCircuitMCMem _ _ (Inner (MC.OtherOp NoOp)) _ bu_env = pure bu_env-shortCircuitMCMem lutab pat (Inner (MC.ParOp (Just par_op) op)) td_env bu_env =- shortCircuitSegOp (const True) lutab pat par_op td_env bu_env- >>= shortCircuitSegOp (const True) lutab pat op td_env-shortCircuitMCMem lutab pat (Inner (MC.ParOp Nothing op)) td_env bu_env =- shortCircuitSegOp (const True) lutab pat op td_env bu_env+shortCircuitMCMem _ _ _ (Alloc _ _) _ bu_env = pure bu_env+shortCircuitMCMem _ _ _ (Inner (MC.OtherOp NoOp)) _ bu_env = pure bu_env+shortCircuitMCMem lutab pat certs (Inner (MC.ParOp (Just par_op) op)) td_env bu_env =+ shortCircuitSegOp (const True) lutab pat certs par_op td_env bu_env+ >>= shortCircuitSegOp (const True) lutab pat certs op td_env+shortCircuitMCMem lutab pat certs (Inner (MC.ParOp Nothing op)) td_env bu_env =+ shortCircuitSegOp (const True) lutab pat certs op td_env bu_env dropLastSegSpace :: SegSpace -> SegSpace dropLastSegSpace space = space {unSegSpace = init $ unSegSpace space}@@ -343,12 +356,13 @@ lvl -> LUTabFun -> Pat (VarAliases, LetDecMem) ->+ Certs -> SegSpace -> KernelBody (Aliases rep) -> TopdownEnv rep -> BotUpEnv -> ShortCircuitM rep BotUpEnv-shortCircuitSegOpHelper num_reds lvlOK lvl lutab pat@(Pat ps0) space0 kernel_body td_env bu_env = do+shortCircuitSegOpHelper num_reds lvlOK lvl lutab pat@(Pat ps0) pat_certs space0 kernel_body td_env bu_env = do -- We need to drop the last element of the 'SegSpace' for pattern elements -- that correspond to reductions. let ps_space_and_res =@@ -366,7 +380,7 @@ (actv_return, inhibit_return) = if num_reds > 0 then (actv0, inhibit0)- else foldl (makeSegMapCoals lvlOK lvl td_env kernel_body) (actv0, inhibit0) ps_space_and_res+ else foldl (makeSegMapCoals lvlOK lvl td_env kernel_body pat_certs) (actv0, inhibit0) ps_space_and_res -- Start from empty references, we'll update with aggregates later. let actv0' = M.map (\etry -> etry {memrefs = mempty}) $ actv0 <> actv_return@@ -503,10 +517,11 @@ lvl -> TopdownEnv rep -> KernelBody (Aliases rep) ->+ Certs -> (CoalsTab, InhibitTab) -> (PatElem (VarAliases, LetDecMem), SegSpace, KernelResult) -> (CoalsTab, InhibitTab)-makeSegMapCoals lvlOK lvl td_env kernel_body (active, inhb) (PatElem pat_name (_, MemArray _ _ _ (ArrayIn pat_mem pat_ixf)), space, Returns _ _ (Var return_name))+makeSegMapCoals lvlOK lvl td_env kernel_body pat_certs (active, inhb) (PatElem pat_name (_, MemArray _ _ _ (ArrayIn pat_mem pat_ixf)), space, Returns _ _ (Var return_name)) | Just (MemBlock tp return_shp return_mem _) <- getScopeMemInfo return_name $ scope td_env <> scopeOf (kernelBodyStms kernel_body), lvlOK lvl,@@ -526,13 +541,13 @@ (MemBlock tp return_shp pat_mem $ resultSlice pat_ixf) mempty & M.singleton return_name- & flip (addInvAliassesVarTab td_env) return_name+ & flip (addInvAliasesVarTab td_env) return_name ) of (False, Just vtab) -> ( active <> M.singleton return_mem- (CoalsEntry pat_mem pat_ixf (oneName pat_mem) vtab mempty mempty),+ (CoalsEntry pat_mem pat_ixf (oneName pat_mem) vtab mempty mempty pat_certs), inhb ) _ -> (active, inhb)@@ -545,7 +560,7 @@ (MemBlock tp return_shp trans_mem $ resultSlice trans_ixf) mempty & M.singleton return_name- & flip (addInvAliassesVarTab td_env) return_name+ & flip (addInvAliasesVarTab td_env) return_name ) of (False, Just vtab) -> let opts =@@ -561,6 +576,7 @@ vtab opts mempty+ (certs trans <> pat_certs) ) active, inhb@@ -572,11 +588,11 @@ & map (DimFix . TPrimExp . flip LeafExp (IntType Int64) . fst) & Slice resultSlice ixf = IxFun.slice ixf $ fullSlice (IxFun.shape ixf) thread_slice-makeSegMapCoals _ _ td_env _ x (_, _, WriteReturns _ _ return_name _) =+makeSegMapCoals _ _ td_env _ _ x (_, _, WriteReturns _ _ return_name _) = case getScopeMemInfo return_name $ scope td_env of Just (MemBlock _ _ return_mem _) -> markFailedCoal x return_mem Nothing -> error "Should not happen?"-makeSegMapCoals _ _ td_env _ x (_, _, result) =+makeSegMapCoals _ _ td_env _ _ x (_, _, result) = freeIn result & namesToList & mapMaybe (flip getScopeMemInfo $ scope td_env)@@ -595,7 +611,9 @@ ShortCircuitM rep CoalsTab fixPointCoalesce lutab fpar bdy topenv = do buenv <- mkCoalsTabStms lutab (bodyStms bdy) topenv (emptyBotUpEnv {inhibit = inhibited topenv})- let (succ_tab, actv_tab, inhb_tab) = (successCoals buenv, activeCoals buenv, inhibit buenv)+ let succ_tab = successCoals buenv+ actv_tab = activeCoals buenv+ inhb_tab = inhibit buenv -- Allow short-circuiting function parameters that are unique and have -- matching index functions, otherwise mark as failed handleFunctionParams (a, i, s) (_, u, MemBlock _ _ m ixf) =@@ -615,12 +633,12 @@ (succ_tab'', failed_optdeps) = fixPointFilterDeps succ_tab' M.empty inhb_tab'' = M.unionWith (<>) failed_optdeps inhb_tab'- in if not $ M.null actv_tab'- then error ("COALESCING ROOT: BROKEN INV, active not empty: " ++ show (M.keys actv_tab'))- else- if M.null $ inhb_tab'' `M.difference` inhibited topenv- then pure succ_tab''- else fixPointCoalesce lutab fpar bdy (topenv {inhibited = inhb_tab''})+ if not $ M.null actv_tab'+ then error ("COALESCING ROOT: BROKEN INV, active not empty: " ++ show (M.keys actv_tab'))+ else+ if M.null $ inhb_tab'' `M.difference` inhibited topenv+ then pure succ_tab''+ else fixPointCoalesce lutab fpar bdy (topenv {inhibited = inhb_tab''}) where fixPointFilterDeps :: CoalsTab -> InhibitTab -> (CoalsTab, InhibitTab) fixPointFilterDeps coaltab inhbtab =@@ -1008,7 +1026,7 @@ foldFunOptimPromotion ((act, inhb), succc) ((b, m_b), (a, m_a), (_r, m_r), (b_i, m_i)) | m_r == m_i, Just info <- M.lookup m_i act,- Just vtab_i <- addInvAliassesVarTab td_env (vartab info) b_i =+ Just vtab_i <- addInvAliasesVarTab td_env (vartab info) b_i = Exc.assert (m_r == m_b && m_a == m_b) ((M.insert m_b (info {vartab = vtab_i}) act, inhb), succc)@@ -1020,7 +1038,7 @@ Just info_a0 <- M.lookup m_a act, Just info_i <- M.lookup m_i act, M.member m_r succc,- Just vtab_i <- addInvAliassesVarTab td_env (vartab info_i) b_i,+ Just vtab_i <- addInvAliasesVarTab td_env (vartab info_i) b_i, [Just info_b, Just info_a] <- map translateIxFnInScope [(b, info_b0), (a, info_a0)] = let info_b' = info_b {optdeps = M.insert b_i m_i $ optdeps info_b} info_a' = info_a {optdeps = M.insert b_i m_i $ optdeps info_a}@@ -1092,97 +1110,87 @@ -- The case of in-place update: -- @let x' = x with slice <- elm@ mkCoalsTabStm lutab stm@(Let pat@(Pat [x']) _ (BasicOp (Update safety x _ _elm))) td_env bu_env- | [(_, MemBlock _ _ m_x _)] <- getArrMemAssoc pat =- do- -- (a) filter by the 3rd safety for @elm@ and @x'@- let (actv, inhbt) = recordMemRefUses td_env bu_env stm- -- (b) if @x'@ is in active coalesced table, then add an entry for @x@ as well- (actv', inhbt') =- case M.lookup m_x actv of- Nothing -> (actv, inhbt)- Just info ->- case M.lookup (patElemName x') (vartab info) of- Nothing ->- markFailedCoal (actv, inhbt) m_x- Just (Coalesced k mblk@(MemBlock _ _ _ x_indfun) _) ->- case freeVarSubstitutions (scope td_env) (scals bu_env) x_indfun of- Just fv_subs- | isInScope td_env (dstmem info) ->- let coal_etry_x = Coalesced k mblk fv_subs- info' =- info- { vartab =- M.insert x coal_etry_x $- M.insert (patElemName x') coal_etry_x (vartab info)- }- in (M.insert m_x info' actv, inhbt)- _ ->- markFailedCoal (actv, inhbt) m_x+ | [(_, MemBlock _ _ m_x _)] <- getArrMemAssoc pat = do+ -- (a) filter by the 3rd safety for @elm@ and @x'@+ let (actv, inhbt) = recordMemRefUses td_env bu_env stm+ -- (b) if @x'@ is in active coalesced table, then add an entry for @x@ as well+ (actv', inhbt') =+ case M.lookup m_x actv of+ Nothing -> (actv, inhbt)+ Just info ->+ case M.lookup (patElemName x') (vartab info) of+ Nothing -> markFailedCoal (actv, inhbt) m_x+ Just (Coalesced k mblk@(MemBlock _ _ _ x_indfun) _) ->+ case freeVarSubstitutions (scope td_env) (scals bu_env) x_indfun of+ Just fv_subs+ | isInScope td_env (dstmem info) ->+ let coal_etry_x = Coalesced k mblk fv_subs+ info' =+ info+ { vartab =+ M.insert x coal_etry_x $+ M.insert (patElemName x') coal_etry_x (vartab info)+ }+ in (M.insert m_x info' actv, inhbt)+ _ ->+ markFailedCoal (actv, inhbt) m_x - -- (c) this stm is also a potential source for coalescing, so process it- actv'' <- if safety == Unsafe then mkCoalsHelper3PatternMatch stm lutab td_env {inhibited = inhbt'} bu_env {activeCoals = actv'} else pure actv'- pure $- bu_env {activeCoals = actv'', inhibit = inhbt'}+ -- (c) this stm is also a potential source for coalescing, so process it+ actv'' <-+ if safety == Unsafe+ then mkCoalsHelper3PatternMatch stm lutab td_env {inhibited = inhbt'} bu_env {activeCoals = actv'}+ else pure actv'+ pure $ bu_env {activeCoals = actv'', inhibit = inhbt'} -- The case of flat in-place update: -- @let x' = x with flat-slice <- elm@ mkCoalsTabStm lutab stm@(Let pat@(Pat [x']) _ (BasicOp (FlatUpdate x _ _elm))) td_env bu_env- | [(_, MemBlock _ _ m_x _)] <- getArrMemAssoc pat =- do- -- (a) filter by the 3rd safety for @elm@ and @x'@- let (actv, inhbt) = recordMemRefUses td_env bu_env stm- -- (b) if @x'@ is in active coalesced table, then add an entry for @x@ as well- (actv', inhbt') =- case M.lookup m_x actv of- Nothing -> (actv, inhbt)- Just info ->- case M.lookup (patElemName x') (vartab info) of- Nothing ->- -- error "In ArrayCoalescing.hs, fun mkCoalsTabStm, case in-place update!"- -- this case should not happen, but if it can that just fail conservatively- markFailedCoal (actv, inhbt) m_x- Just (Coalesced k mblk@(MemBlock _ _ _ x_indfun) _) ->- case freeVarSubstitutions (scope td_env) (scals bu_env) x_indfun of- Just fv_subs- | isInScope td_env (dstmem info) ->- let coal_etry_x = Coalesced k mblk fv_subs- info' =- info- { vartab =- M.insert x coal_etry_x $- M.insert (patElemName x') coal_etry_x (vartab info)- }- in (M.insert m_x info' actv, inhbt)- _ ->- markFailedCoal (actv, inhbt) m_x+ | [(_, MemBlock _ _ m_x _)] <- getArrMemAssoc pat = do+ -- (a) filter by the 3rd safety for @elm@ and @x'@+ let (actv, inhbt) = recordMemRefUses td_env bu_env stm+ -- (b) if @x'@ is in active coalesced table, then add an entry for @x@ as well+ (actv', inhbt') =+ case M.lookup m_x actv of+ Nothing -> (actv, inhbt)+ Just info ->+ case M.lookup (patElemName x') (vartab info) of+ -- this case should not happen, but if it can that+ -- just fail conservatively+ Nothing -> markFailedCoal (actv, inhbt) m_x+ Just (Coalesced k mblk@(MemBlock _ _ _ x_indfun) _) ->+ case freeVarSubstitutions (scope td_env) (scals bu_env) x_indfun of+ Just fv_subs+ | isInScope td_env (dstmem info) ->+ let coal_etry_x = Coalesced k mblk fv_subs+ info' =+ info+ { vartab =+ M.insert x coal_etry_x $+ M.insert (patElemName x') coal_etry_x (vartab info)+ }+ in (M.insert m_x info' actv, inhbt)+ _ ->+ markFailedCoal (actv, inhbt) m_x - -- (c) this stm is also a potential source for coalescing, so process it- actv'' <- mkCoalsHelper3PatternMatch stm lutab td_env {inhibited = inhbt'} bu_env {activeCoals = actv'}- pure $- bu_env {activeCoals = actv'', inhibit = inhbt'}+ -- (c) this stm is also a potential source for coalescing, so process it+ actv'' <- mkCoalsHelper3PatternMatch stm lutab td_env {inhibited = inhbt'} bu_env {activeCoals = actv'}+ pure $ bu_env {activeCoals = actv'', inhibit = inhbt'} -- mkCoalsTabStm _ (Let pat _ (BasicOp Update {})) _ _ = error $ "In ArrayCoalescing.hs, fun mkCoalsTabStm, illegal pattern for in-place update: " ++ show pat -- default handling-mkCoalsTabStm lutab stm@(Let pat _ (Op op)) td_env bu_env = do+mkCoalsTabStm lutab stm@(Let pat aux (Op op)) td_env bu_env = do -- Process body on_op <- asks onOp- activeCoals' <-- mkCoalsHelper3PatternMatch- stm- lutab- td_env- bu_env+ activeCoals' <- mkCoalsHelper3PatternMatch stm lutab td_env bu_env let bu_env' = bu_env {activeCoals = activeCoals'}- on_op lutab pat op td_env bu_env'+ on_op lutab pat (stmAuxCerts aux) op td_env bu_env' mkCoalsTabStm lutab stm@(Let pat _ e) td_env bu_env = do -- i) Filter @activeCoals@ by the 3rd safety condition: -- this is now relaxed by use of LMAD eqs: -- the memory referenced in stm are added to memrefs::dstrefs -- in corresponding coal-tab entries. let (activeCoals', inhibit') = recordMemRefUses td_env bu_env stm- -- mkCoalsHelper1FilterActive pat (freeIn e) (scope td_env) (scals bu_env)- -- (activeCoals bu_env) (inhibit bu_env) -- ii) promote any of the entries in @activeCoals@ to @successCoals@ as long as -- - this statement defined a variable consumed in a coalesced statement@@ -1200,7 +1208,7 @@ foldfun safe_4 ((a_acc, inhb), s_acc) (b, MemBlock tp shp mb _b_indfun) = case M.lookup mb a_acc of Nothing -> ((a_acc, inhb), s_acc)- Just info@(CoalsEntry x_mem _ _ vtab _ _) ->+ Just info@(CoalsEntry x_mem _ _ vtab _ _ certs) -> let failed = markFailedCoal (a_acc, inhb) mb in case M.lookup b vtab of Nothing ->@@ -1211,27 +1219,31 @@ -- @ ... use of b ... @ -- @let c = alias b @ <- currently fails -- @let y[i] = x @- -- where @alias@ can be @transpose@, @slice@, @rotate@, @reshape@.+ -- where @alias@ can be @transpose@, @slice@, @reshape@. -- We use getTransitiveAlias helper function to track the aliasing -- through the td_env, and to find the updated ixfun of @b@: case getDirAliasedIxfn td_env a_acc b of Nothing -> (failed, s_acc) Just (_, _, b_indfun') ->- case freeVarSubstitutions (scope td_env) (scals bu_env) b_indfun' of- Nothing -> (failed, s_acc)- Just fv_subst ->- let mem_info = Coalesced TransitiveCoal (MemBlock tp shp x_mem b_indfun') fv_subst+ case ( freeVarSubstitutions (scope td_env) (scals bu_env) b_indfun',+ freeVarSubstitutions (scope td_env) (scals bu_env) certs+ ) of+ (Just fv_subst, Just fv_subst') ->+ let mem_info = Coalesced TransitiveCoal (MemBlock tp shp x_mem b_indfun') (fv_subst <> fv_subst') info' = info {vartab = M.insert b mem_info vtab} in ((M.insert mb info' a_acc, inhb), s_acc)+ _ -> (failed, s_acc) Just (Coalesced k mblk@(MemBlock _ _ _ new_indfun) _) -> -- we are at the definition of the coalesced variable @b@ -- if 2,4,5 hold promote it to successful coalesced table, -- or if e = transpose, etc. then postpone decision for later on let safe_2 = isInScope td_env x_mem- in case freeVarSubstitutions (scope td_env) (scals bu_env) new_indfun of- Just fv_subst+ in case ( freeVarSubstitutions (scope td_env) (scals bu_env) new_indfun,+ freeVarSubstitutions (scope td_env) (scals bu_env) certs+ ) of+ (Just fv_subst, Just fv_subst') | safe_2 ->- let mem_info = Coalesced k mblk fv_subst+ let mem_info = Coalesced k mblk (fv_subst <> fv_subst') info' = info {vartab = M.insert b mem_info vtab} in if safe_4 then -- array creation point, successful coalescing verified!@@ -1276,7 +1288,7 @@ -- If it is an array in memory block m_b case M.lookup m_b acc of Nothing -> (acc, inhb)- Just info@(CoalsEntry x_mem _ _ vtab _ _) ->+ Just info@(CoalsEntry x_mem _ _ vtab _ _ certs) -> -- And m_b we're trying to coalesce m_b let failed = markFailedCoal (acc, inhb) m_b in -- It is not safe to short circuit if some other pattern@@ -1291,18 +1303,22 @@ Nothing -> failed Just (_, _, b_indfun') -> -- And we have the index function of b- case freeVarSubstitutions (scope td_env) scals_env b_indfun' of- Nothing -> failed- Just fv_subst ->- let mem_info = Coalesced TransitiveCoal (MemBlock tp0 shp0 x_mem b_indfun') fv_subst+ case ( freeVarSubstitutions (scope td_env) scals_env b_indfun',+ freeVarSubstitutions (scope td_env) scals_env certs+ ) of+ (Just fv_subst, Just fv_subst') ->+ let mem_info = Coalesced TransitiveCoal (MemBlock tp0 shp0 x_mem b_indfun') (fv_subst <> fv_subst') info' = info {vartab = M.insert b mem_info vtab} in (M.insert m_b info' acc, inhb)+ _ -> failed Just (Coalesced k (MemBlock pt shp _ new_indfun) _) -> let safe_2 = isInScope td_env x_mem- in case freeVarSubstitutions (scope td_env) scals_env new_indfun of- Just fv_subst+ in case ( freeVarSubstitutions (scope td_env) scals_env new_indfun,+ freeVarSubstitutions (scope td_env) scals_env certs+ ) of+ (Just fv_subst, Just fv_subst') | safe_2 ->- let mem_info = Coalesced k (MemBlock pt shp x_mem new_indfun) fv_subst+ let mem_info = Coalesced k (MemBlock pt shp x_mem new_indfun) (fv_subst <> fv_subst') info' = info {vartab = M.insert b mem_info vtab} in (M.insert m_b info' acc, inhb) _ -> failed@@ -1325,7 +1341,7 @@ where successCoals_tab = successCoals bu_env activeCoals_tab = activeCoals bu_env- processNewCoalesce acc (knd, alias_fn, x, m_x, ind_x, b, m_b, _, tp_b, shp_b) =+ processNewCoalesce acc (knd, alias_fn, x, m_x, ind_x, b, m_b, _, tp_b, shp_b, certs) = -- test whether we are in a transitive coalesced case, i.e., -- @let b = scratch ...@ -- @.....@@@ -1338,17 +1354,17 @@ let proper_coals_tab = case knd of InPlaceCoal -> activeCoals_tab _ -> successCoals_tab- (m_yx, ind_yx, mem_yx_al, x_deps) =+ (m_yx, ind_yx, mem_yx_al, x_deps, certs') = case M.lookup m_x proper_coals_tab of Nothing ->- (m_x, alias_fn ind_x, oneName m_x, M.empty)- Just (CoalsEntry m_y ind_y y_al vtab x_deps0 _) ->+ (m_x, alias_fn ind_x, oneName m_x, M.empty, mempty)+ Just (CoalsEntry m_y ind_y y_al vtab x_deps0 _ certs'') -> let ind = case M.lookup x vtab of Just (Coalesced _ (MemBlock _ _ _ ixf) _) -> ixf Nothing -> ind_y- in (m_y, alias_fn ind, oneName m_x <> y_al, x_deps0)+ in (m_y, alias_fn ind, oneName m_x <> y_al, x_deps0, certs <> certs'') success0 = IxFun.hasOneLmad ind_yx m_b_aliased_m_yx = areAnyAliased td_env m_b [m_yx] -- m_b \= m_yx in if success0 && not m_b_aliased_m_yx && isInScope td_env m_yx -- nameIn m_yx (alloc td_env)@@ -1369,7 +1385,7 @@ then M.empty else M.insert x m_x x_deps vtab = M.singleton b mem_info- mvtab = addInvAliassesVarTab td_env vtab b+ mvtab = addInvAliasesVarTab td_env vtab b is_inhibited = case M.lookup m_b $ inhibited td_env of Just nms -> m_yx `nameIn` nms@@ -1387,6 +1403,7 @@ vtab' opts' mempty+ (certs <> certs') in M.insert m_b coal_etry acc else acc @@ -1401,7 +1418,8 @@ VName, IxFun, PrimType,- Shape+ Shape,+ Certs ) -- | Given an op, return a list of potential short-circuit points@@ -1410,12 +1428,13 @@ TopdownEnv rep -> ScopeTab rep -> Pat (VarAliases, LetDecMem) ->+ Certs -> op -> Maybe [SSPointInfo] genSSPointInfoSeqMem :: GenSSPoint SeqMem (Op (Aliases SeqMem))-genSSPointInfoSeqMem _ _ _ _ _ =+genSSPointInfoSeqMem _ _ _ _ _ _ = Nothing -- | For 'SegOp', we currently only handle 'SegMap', and only under the following@@ -1448,12 +1467,13 @@ td_env scopetab (Pat [PatElem dst (_, MemArray dst_pt _ _ (ArrayIn dst_mem dst_ixf))])+ certs (SegMap _ space _ kernel_body) | (src, MemBlock _ shp src_mem src_ixf) : _ <- mapMaybe getPotentialMapShortCircuit $ stmsToList $ kernelBodyStms kernel_body =- Just [(MapCoal, id, dst, dst_mem, dst_ixf, src, src_mem, src_ixf, dst_pt, shp)]+ Just [(MapCoal, id, dst, dst_mem, dst_ixf, src, src_mem, src_ixf, dst_pt, shp, certs)] where iterators = map fst $ unSegSpace space frees = freeIn kernel_body@@ -1475,31 +1495,31 @@ primBitSize src_pt == primBitSize dst_pt = Just (src, memblock) getPotentialMapShortCircuit _ = Nothing-genSSPointInfoSegOp _ _ _ _ _ =+genSSPointInfoSegOp _ _ _ _ _ _ = Nothing genSSPointInfoMemOp :: GenSSPoint rep (inner (Aliases rep)) -> GenSSPoint rep (MemOp inner (Aliases rep))-genSSPointInfoMemOp onOp lutab td_end scopetab pat (Inner op) =- onOp lutab td_end scopetab pat op-genSSPointInfoMemOp _ _ _ _ _ _ = Nothing+genSSPointInfoMemOp onOp lutab td_end scopetab pat certs (Inner op) =+ onOp lutab td_end scopetab pat certs op+genSSPointInfoMemOp _ _ _ _ _ _ _ = Nothing genSSPointInfoGPUMem :: GenSSPoint GPUMem (Op (Aliases GPUMem)) genSSPointInfoGPUMem = genSSPointInfoMemOp f where- f lutab td_env scopetab pat (GPU.SegOp op) =- genSSPointInfoSegOp lutab td_env scopetab pat op- f _ _ _ _ _ = Nothing+ f lutab td_env scopetab pat certs (GPU.SegOp op) =+ genSSPointInfoSegOp lutab td_env scopetab pat certs op+ f _ _ _ _ _ _ = Nothing genSSPointInfoMCMem :: GenSSPoint MCMem (Op (Aliases MCMem)) genSSPointInfoMCMem = genSSPointInfoMemOp f where- f lutab td_env scopetab pat (MC.ParOp Nothing op) =- genSSPointInfoSegOp lutab td_env scopetab pat op- f _ _ _ _ _ = Nothing+ f lutab td_env scopetab pat certs (MC.ParOp Nothing op) =+ genSSPointInfoSegOp lutab td_env scopetab pat certs op+ f _ _ _ _ _ _ = Nothing genCoalStmtInfo :: Coalesceable rep inner =>@@ -1509,35 +1529,35 @@ Stm (Aliases rep) -> ShortCircuitM rep (Maybe [SSPointInfo]) -- CASE a) @let x <- copy(b^{lu})@-genCoalStmtInfo lutab _ scopetab (Let pat _ (BasicOp (Copy b)))+genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (Replicate (Shape []) (Var b)))) | Pat [PatElem x (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat = pure $ case (M.lookup x lutab, getScopeMemInfo b scopetab) of (Just last_uses, Just (MemBlock tpb shpb m_b ind_b)) -> if b `notNameIn` last_uses then Nothing- else Just [(CopyCoal, id, x, m_x, ind_x, b, m_b, ind_b, tpb, shpb)]+ else Just [(CopyCoal, id, x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)] _ -> Nothing -- CASE c) @let x[i] = b^{lu}@-genCoalStmtInfo lutab _ scopetab (Let pat _ (BasicOp (Update _ x slice_x (Var b))))+genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (Update _ x slice_x (Var b)))) | Pat [PatElem x' (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat = pure $ case (M.lookup x' lutab, getScopeMemInfo b scopetab) of (Just last_uses, Just (MemBlock tpb shpb m_b ind_b)) -> if b `notNameIn` last_uses then Nothing- else Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb)]+ else Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)] _ -> Nothing where updateIndFunSlice :: IxFun -> Slice SubExp -> IxFun updateIndFunSlice ind_fun slc_x = let slc_x' = map (fmap pe64) $ unSlice slc_x in IxFun.slice ind_fun $ Slice slc_x'-genCoalStmtInfo lutab _ scopetab (Let pat _ (BasicOp (FlatUpdate x slice_x b)))+genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (FlatUpdate x slice_x b))) | Pat [PatElem x' (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat = pure $ case (M.lookup x' lutab, getScopeMemInfo b scopetab) of (Just last_uses, Just (MemBlock tpb shpb m_b ind_b)) -> if b `notNameIn` last_uses then Nothing- else Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb)]+ else Just [(InPlaceCoal, (`updateIndFunSlice` slice_x), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)] _ -> Nothing where updateIndFunSlice :: IxFun -> FlatSlice SubExp -> IxFun@@ -1545,7 +1565,7 @@ IxFun.flatSlice ind_fun $ FlatSlice (pe64 offset) $ map (fmap pe64) dims -- CASE b) @let x = concat(a, b^{lu})@-genCoalStmtInfo lutab _ scopetab (Let pat _ (BasicOp (Concat concat_dim (b0 :| bs) _)))+genCoalStmtInfo lutab _ scopetab (Let pat aux (BasicOp (Concat concat_dim (b0 :| bs) _))) | Pat [PatElem x (_, MemArray _ _ _ (ArrayIn m_x ind_x))] <- pat = pure $ case M.lookup x lutab of Nothing -> Nothing@@ -1565,7 +1585,7 @@ map (unitSlice zero . pe64) (take concat_dim dims) <> [unitSlice offs (pe64 d)] <> map (unitSlice zero . pe64) (drop (concat_dim + 1) dims)- in ( acc ++ [(ConcatCoal, (`IxFun.slice` slc), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb)],+ in ( acc ++ [(ConcatCoal, (`IxFun.slice` slc), x, m_x, ind_x, b, m_b, ind_b, tpb, shpb, stmAuxCerts aux)], offs', True )@@ -1575,9 +1595,9 @@ in if null res then Nothing else Just res -- case d) short-circuit points from ops. For instance, the result of a segmap -- can be considered a short-circuit point.-genCoalStmtInfo lutab td_env scopetab (Let pat _ (Op op)) = do+genCoalStmtInfo lutab td_env scopetab (Let pat aux (Op op)) = do ss_op <- asks ssPointFromOp- pure $ ss_op lutab td_env scopetab pat op+ pure $ ss_op lutab td_env scopetab pat (stmAuxCerts aux) op -- CASE other than a), b), c), or d) not supported genCoalStmtInfo _ _ _ _ = pure Nothing
src/Futhark/Optimise/ArrayShortCircuiting/DataStructs.hs view
@@ -45,9 +45,9 @@ import Futhark.Util.Pretty hiding (line, sep, (</>)) import Prelude -type ScopeTab rep = Scope (Aliases rep)--- ^ maps array-variable names to various info, including+-- | maps array-variable names to various info, including -- types, memory block and index function, etc.+type ScopeTab rep = Scope (Aliases rep) -- | An LMAD specialized to TPrimExps (a typed primexp) type LmadRef = IxFun.LMAD (TPrimExp Int64 VName)@@ -161,23 +161,28 @@ optdeps :: M.Map VName VName, -- | Access summaries of uses and writes of destination and source -- respectively.- memrefs :: MemRefs+ memrefs :: MemRefs,+ -- | Certificates of the destination, which must be propagated to+ -- the source. When short-circuiting reaches the array creation+ -- point, we must check whether the certs are in scope for+ -- short-circuiting to succeed.+ certs :: Certs } +-- | the allocatted memory blocks type AllocTab = M.Map VName Space--- ^ the allocatted memory blocks +-- | maps a variable name to its PrimExp scalar expression type ScalarTab = M.Map VName (PrimExp VName)--- ^ maps a variable name to its PrimExp scalar expression -type CoalsTab = M.Map VName CoalsEntry--- ^ maps a memory-block name to a 'CoalsEntry'. Among other things, it contains+-- | maps a memory-block name to a 'CoalsEntry'. Among other things, it contains -- @vartab@, a map in which each variable associated to that memory block is -- bound to its 'Coalesced' info.+type CoalsTab = M.Map VName CoalsEntry -type InhibitTab = M.Map VName Names--- ^ inhibited memory-block mergings from the key (memory block)+-- | inhibited memory-block mergings from the key (memory block) -- to the value (set of memory blocks).+type InhibitTab = M.Map VName Names data BotUpEnv = BotUpEnv { -- | maps scalar variables to theirs PrimExp expansion@@ -242,7 +247,7 @@ -- the same destination memory and use the same index function, the first -- 'CoalsEntry' is returned. unionCoalsEntry :: CoalsEntry -> CoalsEntry -> CoalsEntry-unionCoalsEntry etry1 (CoalsEntry dstmem2 dstind2 alsmem2 vartab2 optdeps2 memrefs2) =+unionCoalsEntry etry1 (CoalsEntry dstmem2 dstind2 alsmem2 vartab2 optdeps2 memrefs2 certs2) = if dstmem etry1 /= dstmem2 || dstind etry1 /= dstind2 then etry1 else@@ -250,7 +255,8 @@ { alsmem = alsmem etry1 <> alsmem2, optdeps = optdeps etry1 <> optdeps2, vartab = vartab etry1 <> vartab2,- memrefs = memrefs etry1 <> memrefs2+ memrefs = memrefs etry1 <> memrefs2,+ certs = certs etry1 <> certs2 } -- | Get the names of array 'PatElem's in a 'Pat' and the corresponding@@ -327,11 +333,9 @@ createsNewArrOK (BasicOp Replicate {}) = True createsNewArrOK (BasicOp Iota {}) = True createsNewArrOK (BasicOp Manifest {}) = True-createsNewArrOK (BasicOp Copy {}) = True createsNewArrOK (BasicOp Concat {}) = True createsNewArrOK (BasicOp ArrayLit {}) = True createsNewArrOK (BasicOp Scratch {}) = True-createsNewArrOK (BasicOp Rotate {}) = True createsNewArrOK _ = False -- | Memory-block removal from active-coalescing table
src/Futhark/Optimise/ArrayShortCircuiting/MemRefAggreg.hs view
@@ -112,12 +112,13 @@ Var a -> case getDirAliasedIxfn td_env coal_tab a of Nothing -> Just ([r1], [r1]) Just r2 -> Just ([r1], [r1, r2])-getUseSumFromStm td_env coal_tab (Let (Pat [y]) _ (BasicOp (Copy x))) = do+getUseSumFromStm td_env coal_tab (Let (Pat [y]) _ (BasicOp (Replicate (Shape []) (Var x)))) = do -- y = copy x wrt <- getDirAliasedIxfn td_env coal_tab $ patElemName y rd <- getDirAliasedIxfn td_env coal_tab x pure ([wrt], [wrt, rd])-getUseSumFromStm _ _ (Let Pat {} _ (BasicOp Copy {})) = error "Impossible"+getUseSumFromStm _ _ (Let Pat {} _ (BasicOp (Replicate (Shape []) _))) =+ error "Impossible" getUseSumFromStm td_env coal_tab (Let (Pat ys) _ (BasicOp (Concat _i (a :| bs) _ses))) = -- concat let ws = mapMaybe (getDirAliasedIxfn td_env coal_tab . patElemName) ys@@ -139,6 +140,12 @@ in case getDirAliasedIxfn td_env coal_tab v of Nothing -> Just ([r1], [r1]) Just r2 -> Just ([r1], [r1, r2])+-- getUseSumFromStm td_env coal_tab (Let (Pat ys) _ (BasicOp bop)) =+-- let wrt = mapMaybe (getDirAliasedIxfn td_env coal_tab . patElemName) ys+-- in trace ("getUseBla: " <> show bop) $ pure (wrt, wrt)+getUseSumFromStm td_env coal_tab (Let (Pat ys) _ (BasicOp Iota {})) =+ let wrt = mapMaybe (getDirAliasedIxfn td_env coal_tab . patElemName) ys+ in pure (wrt, wrt) getUseSumFromStm _ _ (Let Pat {} _ BasicOp {}) = Just ([], []) getUseSumFromStm _ _ (Let Pat {} _ (Op (Alloc _ _))) = Just ([], []) getUseSumFromStm _ _ _ =
src/Futhark/Optimise/ArrayShortCircuiting/TopdownAnalysis.hs view
@@ -11,7 +11,7 @@ updateTopdownEnvLoop, getDirAliasedIxfn, getDirAliasedIxfn',- addInvAliassesVarTab,+ addInvAliasesVarTab, areAnyAliased, isInScope, nonNegativesInPat,@@ -65,14 +65,9 @@ } isInScope :: TopdownEnv rep -> VName -> Bool-isInScope td_env m =- m `M.member` scope td_env+isInScope td_env m = m `M.member` scope td_env -- | Get alias and (direct) index function mapping from expression------ For instance, if the expression is a 'Rotate', returns the value being--- rotated as well as a function for rotating an index function the appropriate--- amount. getDirAliasFromExp :: Exp (Aliases rep) -> Maybe (VName, DirAlias) getDirAliasFromExp (BasicOp (SubExp (Var x))) = Just (x, id) getDirAliasFromExp (BasicOp (Opaque _ (Var x))) = Just (x, id)@@ -82,25 +77,19 @@ Just (x, (`IxFun.reshape` shapeDims (fmap pe64 shp))) getDirAliasFromExp (BasicOp (Rearrange _ _)) = Nothing-getDirAliasFromExp (BasicOp (Rotate _ _)) =- Nothing -- Just (x, (`IxFun.rotate` fmap pe64 rs)) getDirAliasFromExp (BasicOp (Index x slc)) = Just (x, (`IxFun.slice` (Slice $ map (fmap pe64) $ unSlice slc))) getDirAliasFromExp (BasicOp (Update _ x _ _elm)) = Just (x, id) getDirAliasFromExp (BasicOp (FlatIndex x (FlatSlice offset idxs))) = Just ( x,- ( `IxFun.flatSlice`- ( FlatSlice (pe64 offset) $- map (fmap pe64) idxs- )- )+ (`IxFun.flatSlice` FlatSlice (pe64 offset) (map (fmap pe64) idxs)) ) getDirAliasFromExp (BasicOp (FlatUpdate x _ _)) = Just (x, id) getDirAliasFromExp _ = Nothing -- | This was former @createsAliasedArrOK@ from DataStructs--- While Rearrange and Rotate create aliased arrays, we+-- While Rearrange creates aliased arrays, we -- do not yet support them because it would mean we have -- to "reverse" the index function, for example to support -- coalescing in the case below,@@ -190,8 +179,11 @@ nonNegatives = nonNegatives env <> innerNonNegatives (patNames pat) inner, knownLessThan = knownLessThan env <> innerKnownLessThan inner }-updateTopdownEnv env (Let (Pat _) _ (BasicOp (Assert se _ _))) =- env {td_asserts = se : td_asserts env}+updateTopdownEnv env stm@(Let (Pat _) _ (BasicOp (Assert se _ _))) =+ env+ { scope = scope env <> scopeOf stm,+ td_asserts = se : td_asserts env+ } updateTopdownEnv env stm@(Let (Pat [pe]) _ e) | Just (x, ixfn) <- getDirAliasFromExp e = let ixfn_inv = getInvAliasFromExp e@@ -203,9 +195,7 @@ updateTopdownEnv env stm = env { scope = scope env <> scopeOf stm,- nonNegatives =- nonNegatives env- <> nonNegativesInPat (stmPat stm)+ nonNegatives = nonNegatives env <> nonNegativesInPat (stmPat stm) } nonNegativesInPat :: Typed rep => Pat rep -> Names@@ -292,13 +282,13 @@ -- with @x2@ as argument should also insert entries for @x1@ and @x0@ to -- @vartab@, of course if their aliasing operations are invertible. -- We assume inverting aliases has been performed by the top-down pass.-addInvAliassesVarTab ::+addInvAliasesVarTab :: HasMemBlock (Aliases rep) => TopdownEnv rep -> M.Map VName Coalesced -> VName -> Maybe (M.Map VName Coalesced)-addInvAliassesVarTab td_env vtab x+addInvAliasesVarTab td_env vtab x | Just (Coalesced _ (MemBlock _ _ m_y x_ixfun) fv_subs) <- M.lookup x vtab = case M.lookup x (v_alias td_env) of Nothing -> Just vtab@@ -310,8 +300,8 @@ Just (MemBlock ptp shp _ _) -> let coal = Coalesced TransitiveCoal (MemBlock ptp shp m_y x_ixfn0) fv_subs vartab' = M.insert x0 coal vtab- in addInvAliassesVarTab td_env vartab' x0-addInvAliassesVarTab _ _ _ = Nothing+ in addInvAliasesVarTab td_env vartab' x0+addInvAliasesVarTab _ _ _ = Nothing areAliased :: TopdownEnv rep -> VName -> VName -> Bool areAliased _ m_x m_y =
src/Futhark/Optimise/CSE.hs view
@@ -34,7 +34,6 @@ import Control.Monad.Reader import Data.Map.Strict qualified as M-import Data.Maybe (isJust) import Futhark.Analysis.Alias import Futhark.IR import Futhark.IR.Aliases@@ -64,16 +63,23 @@ Bool -> Pass rep rep performCSE cse_arrays =- Pass "CSE" "Combine common subexpressions." $+ Pass "CSE" "Combine common subexpressions." $ \prog -> fmap removeProgAliases- . intraproceduralTransformationWithConsts onConsts onFun+ . intraproceduralTransformationWithConsts+ (onConsts (freeIn (progFuns prog)))+ onFun . aliasAnalysis+ $ prog where- onConsts stms =+ onConsts free_in_funs stms = pure $ fst $ runReader- (cseInStms (consumedInStms stms) (stmsToList stms) (pure ()))+ ( cseInStms+ (free_in_funs <> consumedInStms stms)+ (stmsToList stms)+ (pure ())+ ) (newCSEState cse_arrays) onFun _ = pure . cseInFunDef cse_arrays @@ -126,15 +132,12 @@ runReader (cseInBody ds $ funDefBody fundec) $ newCSEState cse_arrays } where- -- XXX: we treat every non-entry result as a consumption here, because we- -- our core language is not strong enough to fully capture the- -- aliases we want, so we are turning some parts off (see #803,- -- #1241, and the related comment in TypeCheck.hs). This is not a+ -- XXX: we treat every array result as a consumption here, because+ -- it is otherwise complicated to ensure we do not introduce more+ -- aliasing than specified by the return type. This is not a -- practical problem while we still perform such aggressive -- inlining.- ds- | isJust $ funDefEntryPoint fundec = map (diet . declExtTypeOf) $ funDefRetType fundec- | otherwise = map retDiet $ funDefRetType fundec+ ds = map (retDiet . fst) $ funDefRetType fundec retDiet t | primType $ declExtTypeOf t = Observe | otherwise = Consume@@ -217,7 +220,7 @@ CSEState (esubsts, nsubsts) cse_arrays <- ask let e' = normExp $ substituteNames nsubsts e pat' = substituteNames nsubsts pat- if any (bad cse_arrays) $ patElems pat+ if not (alreadyAliases e) && any (bad cse_arrays) (patElems pat) then m [Let pat' (StmAux cs attrs edec) e'] else case M.lookup (edec, e') esubsts of Just (subcs, subpat) -> do@@ -236,6 +239,9 @@ local (addExpSubst pat' edec cs e') $ m [Let pat' (StmAux cs attrs edec) e'] where+ alreadyAliases (BasicOp Index {}) = True+ alreadyAliases (BasicOp Reshape {}) = True+ alreadyAliases _ = False bad cse_arrays pe | Mem {} <- patElemType pe = True | Array {} <- patElemType pe, not cse_arrays = True
src/Futhark/Optimise/DoubleBuffer.hs view
@@ -310,8 +310,7 @@ MemArray pt shape u (ArrayIn _ arg_ixfun) -> do arg_copy <- newVName (baseString arg <> "_dbcopy") letBind (Pat [PatElem arg_copy $ MemArray pt shape u $ ArrayIn mem' arg_ixfun]) $- BasicOp $- Copy arg+ BasicOp (Replicate mempty $ Var arg) -- We need to make this parameter unique to avoid invalid -- hoisting (see #1533), because we are invalidating the -- underlying memory.@@ -432,7 +431,10 @@ let bt = elemType $ paramType f shape = arrayShape $ paramType f bound = MemArray bt shape NoUniqueness $ ArrayIn mem v_ixfun- tell [Let (Pat [PatElem v_copy bound]) (defAux ()) $ BasicOp $ Copy v]+ tell+ [ Let (Pat [PatElem v_copy bound]) (defAux ()) $+ BasicOp (Replicate mempty $ Var v)+ ] -- It is important that we treat this as a consumption, to -- avoid the Copy from being hoisted out of any enclosing -- loops. Since we re-use (=overwrite) memory in the loop,@@ -464,9 +466,10 @@ let t = resultType $ paramType fparam summary = MemArray (elemType t) (arrayShape t) NoUniqueness $ ArrayIn bufname ixfun copystm =- Let (Pat [PatElem copyname summary]) (defAux ()) $- BasicOp $- Copy v+ Let+ (Pat [PatElem copyname summary])+ (defAux ())+ (BasicOp $ Replicate mempty $ Var v) in (Just copystm, SubExpRes cs (Var copyname)) buffer _ _ se = (Nothing, se)
src/Futhark/Optimise/Fusion.hs view
@@ -219,7 +219,7 @@ makeCopyStms vs = do vs' <- mapM makeNewName vs copies <- forM (zip vs vs') $ \(name, name') ->- mkLetNames [name'] $ BasicOp $ Copy name+ mkLetNames [name'] $ BasicOp $ Replicate mempty $ Var name pure (stmsFromList copies, M.fromList $ zip vs vs') where makeNewName name = newVName $ baseString name <> "_copy"
src/Futhark/Optimise/GenRedOpt.hs view
@@ -419,7 +419,6 @@ costRedundantStmt (Let _ _ (BasicOp Update {})) = Break costRedundantStmt (Let _ _ (BasicOp FlatUpdate {})) = Break costRedundantStmt (Let _ _ (BasicOp Concat {})) = Big-costRedundantStmt (Let _ _ (BasicOp Copy {})) = Big costRedundantStmt (Let _ _ (BasicOp Manifest {})) = Big costRedundantStmt (Let _ _ (BasicOp Replicate {})) = Big costRedundantStmt (Let _ _ (BasicOp UpdateAcc {})) = Break
src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs view
@@ -164,7 +164,7 @@ ret', stmsFromList [ mkLet [Ident v_aliased $ typeOf v_dec] $ BasicOp $ Index bindee_nm slice,- mkLet [Ident v $ typeOf v_dec] $ BasicOp $ Copy v_aliased+ mkLet [Ident v $ typeOf v_dec] $ BasicOp $ Replicate mempty $ Var v_aliased ] ) onRet pe ret =@@ -267,7 +267,7 @@ Index (updateName update) (fullSlice source_t $ unSlice $ updateIndices update),- mkLet [Ident (updateValue update) elm_t] $ BasicOp $ Copy precopy+ mkLet [Ident (updateValue update) elm_t] $ BasicOp $ Replicate mempty $ Var precopy ] ) pure $
src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs view
@@ -58,20 +58,6 @@ IndexSubstitutions -> Stm (Rep m) -> m IndexSubstitutions--- FIXME: we likely need to do something similar for all expressions--- that produce aliases. Ugh. See issue #1460. Or maybe we should--- look at/copy all consumed arrays up front, instead of ad-hoc.-substituteIndicesInStm substs (Let pat _ (BasicOp (Rotate rots v)))- | Just (cs, src, src_t, is) <- lookup v substs,- [v'] <- patNames pat = do- src' <-- letExp (baseString v' <> "_subst") $- BasicOp $- Rotate (replicate (arrayRank src_t - length rots) zero ++ rots) src- src_t' <- lookupType src'- pure $ (v', (cs, src', src_t', is)) : substs- where- zero = intConst Int64 0 substituteIndicesInStm substs (Let pat _ (BasicOp (Rearrange perm v))) | Just (cs, src, src_t, is) <- lookup v substs, [v'] <- patNames pat = do@@ -119,12 +105,13 @@ | Just (cs2, src2, src2dec, is2) <- lookup v substs = do row <- certifying cs2 $- letExp (baseString v ++ "_row") $+ letSubExp (baseString v ++ "_row") $ BasicOp $ Index src2 $ fullSlice (typeOf src2dec) (unSlice is2) row_copy <-- letExp (baseString v ++ "_row_copy") $ BasicOp $ Copy row+ letExp (baseString v ++ "_row_copy") . BasicOp $+ Replicate mempty row pure $ update v
src/Futhark/Optimise/ReduceDeviceSyncs/MigrationTable.hs view
@@ -184,9 +184,9 @@ -- include user defined functions that could turn out to be host-only. checkFunDef :: FunDef GPU -> Maybe (Set Name) checkFunDef fun = do- checkFParams (funDefParams fun)- checkRetTypes (funDefRetType fun)- checkBody (funDefBody fun)+ checkFParams $ funDefParams fun+ checkRetTypes $ map fst $ funDefRetType fun+ checkBody $ funDefBody fun where hostOnly = Nothing ok = Just ()@@ -249,7 +249,7 @@ analyseFunDef :: HostOnlyFuns -> FunDef GPU -> MigrationTable analyseFunDef hof fd = let body = funDefBody fd- usage = foldl' f [] $ zip (bodyResult body) (funDefRetType fd)+ usage = foldl' f [] $ zip (bodyResult body) (map fst $ funDefRetType fd) stms = bodyStms body in analyseStms hof usage stms where@@ -473,15 +473,6 @@ BasicOp (Rearrange _ arr) -> do graphInefficientReturn [] e one bs `reuses` arr- BasicOp (Rotate _ arr) -> do- -- Migrating a Rotate leads to a memory allocation error.- --- -- TODO: Fix Rotate memory allocation error.- --- -- Can be replaced with 'graphHostOnly e' to disable migration.- -- A fix can be verified by enabling tests/migration/reuse7_rotate.fut- graphInefficientReturn [] e- one bs `reuses` arr -- Expressions with a cost linear to the size of their result arrays are -- inefficient to migrate into GPUBody kernels as such kernels are single- -- threaded. For sufficiently large arrays the cost may exceed what is saved@@ -500,9 +491,6 @@ BasicOp Concat {} -> -- Is unlikely to prevent a scalar read as the only SubExp operand in -- practice is a computation of host-only size variables.- graphHostOnly e- BasicOp Copy {} ->- -- Only takes an array operand, so cannot directly prevent a scalar read. graphHostOnly e BasicOp Manifest {} -> -- Takes no scalar operands so cannot directly prevent a scalar read.
src/Futhark/Optimise/Simplify/Engine.hs view
@@ -66,6 +66,7 @@ import Control.Monad import Control.Monad.Reader import Control.Monad.State.Strict+import Data.Bitraversable import Data.Either import Data.List (find, foldl', inits, mapAccumL) import Data.Map qualified as M@@ -650,7 +651,6 @@ cheapExp (BasicOp CmpOp {}) = True cheapExp (BasicOp ConvOp {}) = True cheapExp (BasicOp Assert {}) = True-cheapExp (BasicOp Copy {}) = False cheapExp (BasicOp Replicate {}) = False cheapExp (BasicOp Concat {}) = False cheapExp (BasicOp Manifest {}) = False@@ -709,7 +709,6 @@ -- Hoist things that are free. isNotHoistableBnd _ _ (Let _ _ (BasicOp Reshape {})) = False isNotHoistableBnd _ _ (Let _ _ (BasicOp Rearrange {})) = False- isNotHoistableBnd _ _ (Let _ _ (BasicOp Rotate {})) = False isNotHoistableBnd _ _ (Let _ _ (BasicOp (Index _ slice))) = null $ sliceDims slice --@@ -1123,8 +1122,22 @@ FunDef (Wise rep) -> SimpleM rep (FunDef (Wise rep)) simplifyFun (FunDef entry attrs fname rettype params body) = do- rettype' <- simplify rettype+ rettype' <- mapM (bitraverse simplify pure) rettype params' <- mapM (traverse simplify) params- let usages = map (usageFromDiet . diet . declExtTypeOf) rettype'+ let usages = map usageFromRet rettype' body' <- bindFParams params $ simplifyBodyNoHoisting mempty usages body pure $ FunDef entry attrs fname rettype' params' body'+ where+ aliasable Array {} = True+ aliasable _ = False+ aliasable_params =+ map snd $ filter (aliasable . paramType . fst) $ zip params [0 ..]+ aliasable_rets =+ map snd $ filter (aliasable . extTypeOf . fst . fst) $ zip rettype [0 ..]+ restricted als = any (`notElem` als)+ usageFromRet (t, RetAls pals rals) =+ usageFromDiet (diet $ declExtTypeOf t)+ <> if restricted pals aliasable_params+ || restricted rals aliasable_rets+ then UT.consumedU+ else mempty
src/Futhark/Optimise/Simplify/Rep.hs view
@@ -302,7 +302,7 @@ addOpWisdom :: Informing rep => op rep -> op (Wise rep) instance CanBeWise NoOp where- addOpWisdom _ = undefined+ addOpWisdom NoOp = NoOp -- | Construct a 'Wise' statement. informStm :: Informing rep => Stm rep -> Stm (Wise rep)
src/Futhark/Optimise/Simplify/Rules.hs view
@@ -63,7 +63,7 @@ -- -- This simplistic rule is only valid before we introduce memory. removeUnnecessaryCopy :: BuilderOps rep => BottomUpRuleBasicOp rep-removeUnnecessaryCopy (vtable, used) (Pat [d]) aux (Copy v)+removeUnnecessaryCopy (vtable, used) (Pat [d]) aux (Replicate (Shape []) (Var v)) | not (v `UT.isConsumed` used), -- This two first clauses below are too conservative, but the -- problem is that 'v' might not look like it has been consumed if@@ -71,8 +71,7 @@ -- simplifier applies bottom-up rules in a kind of deepest-first -- order. not (patElemName d `UT.isInResult` used)- || patElemName d- `UT.isConsumed` used+ || (patElemName d `UT.isConsumed` used) -- Always OK to remove the copy if 'v' has no aliases and is never -- used again. || (v_is_fresh && v_not_used_again),@@ -127,13 +126,14 @@ simplifyIndex :: BuilderOps rep => BottomUpRuleBasicOp rep simplifyIndex (vtable, used) pat@(Pat [pe]) (StmAux cs attrs _) (Index idd inds)- | Just m <- simplifyIndexing vtable seType idd inds consumed = Simplify $ do- res <- certifying cs m- attributing attrs $ case res of- SubExpResult cs' se ->- certifying cs' $ letBindNames (patNames pat) $ BasicOp $ SubExp se- IndexResult extra_cs idd' inds' ->- certifying extra_cs $ letBindNames (patNames pat) $ BasicOp $ Index idd' inds'+ | Just m <- simplifyIndexing vtable seType idd inds consumed =+ Simplify $ certifying cs $ do+ res <- m+ attributing attrs $ case res of+ SubExpResult cs' se ->+ certifying cs' $ letBindNames (patNames pat) $ BasicOp $ SubExp se+ IndexResult extra_cs idd' inds' ->+ certifying extra_cs $ letBindNames (patNames pat) $ BasicOp $ Index idd' inds' where consumed = patElemName pe `UT.isConsumed` used seType (Var v) = ST.lookupType v vtable@@ -307,4 +307,4 @@ -- That's it! We then let ordinary dead code elimination eventually -- simplify the body enough that we have an "identity" WithAcc. There -- is no _guarantee_ that this will happen, but our general dead code--- elimination tends to be prettyString good.+-- elimination tends to be pretty good.
src/Futhark/Optimise/Simplify/Rules/BasicOp.hs view
@@ -99,7 +99,7 @@ -- may be produced as a result of other simplification rules. simplifyConcat _ pat aux (Concat _ (x :| []) _) = -- Still need a copy because Concat produces a fresh array.- Simplify $ auxing aux $ letBind pat $ BasicOp $ Copy x+ Simplify $ auxing aux $ letBind pat $ BasicOp $ Replicate mempty $ Var x -- concat xs (concat ys zs) == concat xs ys zs simplifyConcat (vtable, _) pat (StmAux cs attrs _) (Concat i (x :| xs) new_d) | x' /= x || concat xs' /= xs =@@ -173,7 +173,7 @@ arrayFrom e = Simplify $ auxing aux $ letBind pat $ BasicOp $ SubExp $ Var dest where- arrayFrom (BasicOp (Copy copy_v))+ arrayFrom (BasicOp (Replicate (Shape []) (Var copy_v))) | Just (e', _) <- ST.lookupExp copy_v vtable = arrayFrom e' arrayFrom (BasicOp (Index src srcis)) =@@ -191,13 +191,13 @@ case se of Var v | not $ null $ sliceDims is -> do v_reshaped <-- letExp (baseString v ++ "_reshaped") . BasicOp $+ letSubExp (baseString v ++ "_reshaped") . BasicOp $ Reshape ReshapeArbitrary (arrayShape dest_t) v- letBind pat $ BasicOp $ Copy v_reshaped+ letBind pat $ BasicOp $ Replicate mempty v_reshaped _ -> letBind pat $ BasicOp $ ArrayLit [se] $ rowType dest_t ruleBasicOp vtable pat (StmAux cs1 attrs _) (Update safety1 dest1 is1 (Var v1)) | Just (Update safety2 dest2 is2 se2, cs2) <- ST.lookupBasicOp v1 vtable,- Just (Copy v3, cs3) <- ST.lookupBasicOp dest2 vtable,+ Just (Replicate (Shape []) (Var v3), cs3) <- ST.lookupBasicOp dest2 vtable, Just (Index v4 is4, cs4) <- ST.lookupBasicOp v3 vtable, is4 == is1, v4 == dest1 =@@ -236,20 +236,17 @@ fmap snd . find ((== v) . patElemName . fst) $ zip (patElems ifpat) $ zip (map resSubExp (bodyResult tbranch)) (map resSubExp (bodyResult fbranch))-ruleBasicOp _ pat _ (Replicate (Shape []) se@Constant {}) =- Simplify $ letBind pat $ BasicOp $ SubExp se ruleBasicOp _ pat _ (Replicate _ se) | [Acc {}] <- patTypes pat = Simplify $ letBind pat $ BasicOp $ SubExp se-ruleBasicOp _ pat _ (Replicate (Shape []) (Var v)) = Simplify $ do- v_t <- lookupType v- letBind pat $- BasicOp $- if primType v_t- then SubExp $ Var v- else Copy v+ruleBasicOp _ pat _ (Replicate (Shape []) se) = Simplify $ do+ se_t <- subExpType se+ if primType se_t+ then letBind pat $ BasicOp $ SubExp se+ else cannotSimplify ruleBasicOp vtable pat _ (Replicate shape (Var v))- | Just (BasicOp (Replicate shape2 se), cs) <- ST.lookupExp v vtable =+ | Just (BasicOp (Replicate shape2 se), cs) <- ST.lookupExp v vtable,+ ST.subExpAvailable se vtable = Simplify $ certifying cs $ letBind pat $ BasicOp $ Replicate (shape <> shape2) se ruleBasicOp _ pat _ (ArrayLit (se : ses) _) | all (== se) ses =@@ -283,7 +280,7 @@ map DimFix new_inds' -- Copying an iota is pointless; just make it an iota instead.-ruleBasicOp vtable pat aux (Copy v)+ruleBasicOp vtable pat aux (Replicate (Shape []) (Var v)) | Just (Iota n x s it, v_cs) <- ST.lookupBasicOp v vtable = Simplify . certifying v_cs . auxing aux $ letBind pat $@@ -300,17 +297,6 @@ letBind pat $ BasicOp $ Rearrange (perm `rearrangeCompose` perm2) e-ruleBasicOp vtable pat aux (Rearrange perm v)- | Just (BasicOp (Rotate offsets v2), v_cs) <- ST.lookupExp v vtable,- Just (BasicOp (Rearrange perm3 v3), v2_cs) <- ST.lookupExp v2 vtable = Simplify $ do- let offsets' = rearrangeShape (rearrangeInverse perm3) offsets- rearrange_rotate <- letExp "rearrange_rotate" $ BasicOp $ Rotate offsets' v3- certifying (v_cs <> v2_cs) $- auxing aux $- letBind pat $- BasicOp $- Rearrange (perm `rearrangeCompose` perm3) rearrange_rotate- -- Rearranging a replicate where the outer dimension is left untouched. ruleBasicOp vtable pat aux (Rearrange perm v1) | Just (BasicOp (Replicate dims (Var v2)), v1_cs) <- ST.lookupExp v1 vtable,@@ -327,34 +313,6 @@ Rearrange (map (subtract num_dims) rest_perm) v2 letBind pat $ BasicOp $ Replicate dims v --- A zero-rotation is identity.-ruleBasicOp _ pat _ (Rotate offsets v)- | all isCt0 offsets = Simplify $ letBind pat $ BasicOp $ SubExp $ Var v-ruleBasicOp vtable pat aux (Rotate offsets v)- | Just (BasicOp (Rearrange perm v2), v_cs) <- ST.lookupExp v vtable,- Just (BasicOp (Rotate offsets2 v3), v2_cs) <- ST.lookupExp v2 vtable = Simplify $ do- let offsets2' = rearrangeShape (rearrangeInverse perm) offsets2- addOffsets x y = letSubExp "summed_offset" $ BasicOp $ BinOp (Add Int64 OverflowWrap) x y- offsets' <- zipWithM addOffsets offsets offsets2'- rotate_rearrange <-- auxing aux $ letExp "rotate_rearrange" $ BasicOp $ Rearrange perm v3- certifying (v_cs <> v2_cs) $- letBind pat $- BasicOp $- Rotate offsets' rotate_rearrange---- Combining Rotates.-ruleBasicOp vtable pat aux (Rotate offsets1 v)- | Just (BasicOp (Rotate offsets2 v2), v_cs) <- ST.lookupExp v vtable = Simplify $ do- offsets <- zipWithM add offsets1 offsets2- certifying v_cs $- auxing aux $- letBind pat $- BasicOp $- Rotate offsets v2- where- add x y = letSubExp "offset" $ BasicOp $ BinOp (Add Int64 OverflowWrap) x y- -- Simplify away 0<=i when 'i' is from a loop of form 'for i < n'. ruleBasicOp vtable pat aux (CmpOp CmpSle {} x y) | Constant (IntValue (Int64Value 0)) <- x,@@ -396,7 +354,7 @@ -- Manifest of a a copy can be simplified to manifesting the original -- array, if it is still available. ruleBasicOp vtable pat aux (Manifest perm v1)- | Just (Copy v2, cs) <- ST.lookupBasicOp v1 vtable,+ | Just (Replicate (Shape []) (Var v2), cs) <- ST.lookupBasicOp v1 vtable, ST.available v2 vtable = Simplify . auxing aux . certifying cs $ letBind pat $
src/Futhark/Optimise/Simplify/Rules/ClosedForm.hs view
@@ -206,7 +206,7 @@ zip (map paramName arrparams) arrs isReplicate (p, v)- | Just (BasicOp (Replicate _ ve), cs) <- look v,+ | Just (BasicOp (Replicate (Shape (_ : _)) ve), cs) <- look v, cs == mempty = Just (p, ve) isReplicate _ = Nothing
src/Futhark/Optimise/Simplify/Rules/Index.hs view
@@ -94,22 +94,6 @@ letSubExp "slice_iota" $ BasicOp $ Iota i_n i_offset'' i_stride'' to_it-- -- A rotate cannot be simplified away if we are slicing a rotated dimension.- Just (Rotate offsets a, cs)- | not $ or $ zipWith rotateAndSlice offsets inds -> Just $ do- dims <- arrayDims <$> lookupType a- let adjustI i o d = do- i_p_o <- letSubExp "i_p_o" $ BasicOp $ BinOp (Add Int64 OverflowWrap) i o- letSubExp "rot_i" (BasicOp $ BinOp (SMod Int64 Unsafe) i_p_o d)- adjust (DimFix i, o, d) =- DimFix <$> adjustI i o d- adjust (DimSlice i n s, o, d) =- DimSlice <$> adjustI i o d <*> pure n <*> pure s- IndexResult cs a . Slice <$> mapM adjust (zip3 inds offsets dims)- where- rotateAndSlice r DimSlice {} = not $ isCt0 r- rotateAndSlice _ _ = False Just (Index aa ais, cs) -> Just $ IndexResult cs aa@@ -128,7 +112,8 @@ Just (Replicate (Shape ds) v, cs) | (ds_inds, rest_inds) <- splitAt (length ds) inds, (ds', ds_inds') <- unzip $ mapMaybe index ds_inds,- ds' /= ds ->+ ds' /= ds,+ ST.subExpAvailable v vtable -> Just $ do arr <- letExp "smaller_replicate" $ BasicOp $ Replicate (Shape ds') v pure $ IndexResult cs arr $ Slice $ ds_inds' ++ rest_inds@@ -142,7 +127,7 @@ where isIndex DimFix {} = True isIndex _ = False- Just (Copy src, cs)+ Just (Replicate (Shape []) (Var src), cs) | Just dims <- arrayDims <$> seType (Var src), length inds == length dims, -- It is generally not safe to simplify a slice of a copy,
src/Futhark/Optimise/Simplify/Rules/Loop.hs view
@@ -78,7 +78,7 @@ dummyStm ((p, e), _) | unique (paramDeclType p), Var v <- e =- ([paramName p], BasicOp $ Copy v)+ ([paramName p], BasicOp $ Replicate mempty $ Var v) | otherwise = ([paramName p], BasicOp $ SubExp e) removeRedundantMergeVariables _ _ _ _ = Skip
src/Futhark/Optimise/Simplify/Rules/Simple.hs view
@@ -339,13 +339,13 @@ Just (Update safety dest slice' $ Var v', v_cs) simplifyUpdateReshape _ _ _ = Nothing --- | If we are copying a scratch array (possibly indirectly), just turn it into a scratch by--- itself.-copyScratchToScratch :: SimpleRule rep-copyScratchToScratch defOf seType (Copy src) = do+-- | If we are replicating a scratch array (possibly indirectly), just+-- turn it into a scratch by itself.+repScratchToScratch :: SimpleRule rep+repScratchToScratch defOf seType (Replicate shape (Var src)) = do t <- seType $ Var src cs <- isActuallyScratch src- pure (Scratch (elemType t) (arrayDims t), cs)+ pure (Scratch (elemType t) (shapeDims shape <> arrayDims t), cs) where isActuallyScratch v = case defOf v of@@ -356,7 +356,7 @@ Just (BasicOp (Reshape _ _ v'), cs) -> (cs <>) <$> isActuallyScratch v' _ -> Nothing-copyScratchToScratch _ _ _ =+repScratchToScratch _ _ _ = Nothing simpleRules :: [SimpleRule rep]@@ -366,7 +366,7 @@ simplifyUnOp, simplifyConvOp, simplifyAssert,- copyScratchToScratch,+ repScratchToScratch, simplifyIdentityReshape, simplifyReshapeReshape, simplifyReshapeScratch,
src/Futhark/Optimise/TileLoops.hs view
@@ -261,7 +261,11 @@ -- results that are views of an array (slicing, rotate, etc) and which -- results are used after the prelude, because these cannot be -- efficiently represented by a scalar segmap (they'll be manifested--- in memory).+-- in memory). To avoid unnecessarily moving computation from+-- category 2 to category 3 simply because they depend on a category 3+-- result, everything in category 3 is also in category 2. This is+-- efficient only when category 3 contains exclusively "free" or at+-- least very cheap expressions (e.g. index space transformations). partitionPrelude :: VarianceTable -> Stms GPU ->@@ -269,21 +273,19 @@ Names -> (Stms GPU, Stms GPU, Stms GPU) partitionPrelude variance prestms private used_after =- (invariant_prestms, precomputed_variant_prestms, recomputed_variant_prestms)+ (invariant_prestms, variant_prestms, recomputed_variant_prestms) where invariantTo names stm = case patNames (stmPat stm) of [] -> True -- Does not matter. v : _ -> all (`notNameIn` names) (namesToList $ M.findWithDefault mempty v variance) + consumed_in_prestms =+ foldMap consumedInStm $ fst $ Alias.analyseStms mempty prestms consumed v = v `nameIn` consumed_in_prestms consumedStm stm = any consumed (patNames (stmPat stm))- later_consumed =- namesFromList $- concatMap (patNames . stmPat) $- stmsToList $- Seq.filter consumedStm prestms+ namesFromList $ foldMap (patNames . stmPat) $ Seq.filter consumedStm prestms groupInvariant stm = invariantTo private stm@@ -292,29 +294,22 @@ (invariant_prestms, variant_prestms) = Seq.partition groupInvariant prestms - consumed_in_prestms =- foldMap consumedInStm $ fst $ Alias.analyseStms mempty prestms- mustBeInlinedExp (BasicOp (Index _ slice)) = not $ null $ sliceDims slice mustBeInlinedExp (BasicOp Iota {}) = True- mustBeInlinedExp (BasicOp Rotate {}) = True mustBeInlinedExp (BasicOp Rearrange {}) = True mustBeInlinedExp (BasicOp Reshape {}) = True mustBeInlinedExp _ = False mustBeInlined stm = mustBeInlinedExp (stmExp stm) && any (`nameIn` used_after) (patNames (stmPat stm))- must_be_inlined = namesFromList $- concatMap (patNames . stmPat) $- stmsToList $- Seq.filter mustBeInlined variant_prestms+ foldMap (patNames . stmPat) $+ Seq.filter mustBeInlined variant_prestms recompute stm = any (`nameIn` must_be_inlined) (patNames (stmPat stm))- || not (invariantTo must_be_inlined stm)- (recomputed_variant_prestms, precomputed_variant_prestms) =- Seq.partition recompute variant_prestms+ recomputed_variant_prestms =+ Seq.filter recompute variant_prestms -- Anything that is variant to the "private" names should be -- considered thread-local.
src/Futhark/Pass.hs view
@@ -49,7 +49,7 @@ -- name via 'passLongOption'. passName :: String, -- | A slightly longer description, which will show up in the- -- command-line help pretty.+ -- command-line --help option. passDescription :: String, passFunction :: Prog fromrep -> PassM (Prog torep) }
src/Futhark/Pass/AD.hs view
@@ -28,8 +28,8 @@ bindLambda pat aux (Lambda params body _) args = do auxing aux . forM_ (zip params args) $ \(param, arg) -> letBindNames [paramName param] $- BasicOp $ case (paramType param, arg) of- (Array {}, Var v) -> Copy v+ BasicOp $ case paramType param of+ Array {} -> Replicate mempty arg _ -> SubExp arg res <- bodyBind body forM_ (zip (patNames pat) res) $ \(v, SubExpRes cs se) ->
src/Futhark/Pass/ExpandAllocations.hs view
@@ -824,7 +824,7 @@ let substs = M.fromList (zip consumed consumed') addStms $ substituteNames substs stms where- copy v = letExp (baseString v <> "_copy") $ BasicOp $ Copy v+ copy v = letExp (baseString v <> "_copy") $ BasicOp $ Replicate mempty $ Var v -- Important for edge cases (#1838) that the Stms here still have the -- Allocs we are actually trying to get rid of.
src/Futhark/Pass/ExplicitAllocations.hs view
@@ -483,7 +483,8 @@ let Array pt shape u = v_t mem <- allocForArray' v_t space v_copy <- newVName $ baseString v <> "_scalcopy"- letBind (Pat [PatElem v_copy $ MemArray pt shape u $ ArrayIn mem ixfun]) $ BasicOp $ Copy v+ let pe = PatElem v_copy $ MemArray pt shape u $ ArrayIn mem ixfun+ letBind (Pat [pe]) $ BasicOp $ Replicate mempty $ Var v pure (mem, v_copy) ensureDirectArray ::@@ -563,6 +564,10 @@ linearFuncallArg _ _ arg = pure arg +shiftRetAls :: Int -> Int -> RetAls -> RetAls+shiftRetAls a b (RetAls is js) =+ RetAls (map (+ a) is) (map (+ b) js)+ explicitAllocationsGeneric :: (Allocable fromrep torep inner) => Space ->@@ -581,7 +586,13 @@ allocInFParams (zip params $ repeat space) $ \params' -> do (fbody', mem_rets) <- allocInFunBody (map (const $ Just space) rettype) fbody- let rettype' = mem_rets ++ memoryInDeclExtType space (length mem_rets) rettype+ let num_extra_params = length params' - length params+ num_extra_rets = length mem_rets+ rettype' =+ map (,RetAls mempty mempty) mem_rets+ ++ zip+ (memoryInDeclExtType space (length mem_rets) (map fst rettype))+ (map (shiftRetAls num_extra_params num_extra_rets . snd) rettype) pure $ FunDef entry attrs fname rettype' params' fbody' explicitAllocationsInStmsGeneric ::@@ -901,10 +912,16 @@ args' <- funcallArgs args space <- askDefaultSpace -- We assume that every array is going to be in its own memory.- pure $ Apply fname args' (mems space ++ memoryInDeclExtType space num_arrays rettype) loc+ let num_extra_args = length args' - length args+ rettype' =+ mems space+ ++ zip+ (memoryInDeclExtType space num_arrays (map fst rettype))+ (map (shiftRetAls num_extra_args num_arrays . snd) rettype)+ pure $ Apply fname args' rettype' loc where- mems space = replicate num_arrays (MemMem space)- num_arrays = length $ filter ((> 0) . arrayRank . declExtTypeOf) rettype+ mems space = replicate num_arrays (MemMem space, RetAls mempty mempty)+ num_arrays = length $ filter ((> 0) . arrayRank . declExtTypeOf . fst) rettype allocInExp (Match ses cases defbody (MatchDec rets ifsort)) = do (defbody', def_reqs) <- allocInMatchBody rets defbody (cases', cases_reqs) <- mapAndUnzipM onCase cases
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -188,7 +188,8 @@ stmsFromList $ zipWith identityStm (patElems rem_pat) res identityStm pe (SubExpRes cs (Var v)) | Just arr <- lookup v params_to_arrs =- certify cs $ Let (Pat [pe]) (defAux ()) $ BasicOp $ Copy arr+ certify cs . Let (Pat [pe]) (defAux ()) . BasicOp $+ Replicate mempty (Var arr) identityStm pe (SubExpRes cs se) = certify cs . Let (Pat [pe]) (defAux ()) . BasicOp $ Replicate (Shape [loopNestingWidth outermost]) se@@ -260,9 +261,11 @@ remnantStm pe (SubExpRes cs (Var v)) | Just (_, arr) <- find ((== v) . paramName . fst) inps =- certify cs $ Let (Pat [pe]) aux $ BasicOp $ Copy arr+ certify cs . Let (Pat [pe]) aux . BasicOp $+ Replicate mempty (Var arr) remnantStm pe (SubExpRes cs se) =- certify cs $ Let (Pat [pe]) aux $ BasicOp $ Replicate (Shape [w]) se+ certify cs . Let (Pat [pe]) aux . BasicOp $+ Replicate (Shape [w]) se stms = stmsFromList $ zipWith remnantStm (patElems pat) res@@ -577,15 +580,15 @@ lambdaBody = mkBody (oneStm tmpstm) [varRes tmp] } maybeDistributeStm newstm acc-maybeDistributeStm stm@(Let _ aux (BasicOp (Copy stm_arr))) acc =+maybeDistributeStm stm@(Let _ aux (BasicOp (Replicate (Shape []) (Var stm_arr)))) acc = distributeSingleUnaryStm acc stm stm_arr $ \_ outerpat arr ->- pure $ oneStm $ Let outerpat aux $ BasicOp $ Copy arr+ pure $ oneStm $ Let outerpat aux $ BasicOp $ Replicate mempty $ Var arr -- Opaques are applied to the full array, because otherwise they can -- drastically inhibit parallelisation in some cases. maybeDistributeStm stm@(Let (Pat [pe]) aux (BasicOp (Opaque _ (Var stm_arr)))) acc | not $ primType $ typeOf pe = distributeSingleUnaryStm acc stm stm_arr $ \_ outerpat arr ->- pure $ oneStm $ Let outerpat aux $ BasicOp $ Copy arr+ pure $ oneStm $ Let outerpat aux $ BasicOp $ Replicate mempty $ Var arr maybeDistributeStm stm@(Let _ aux (BasicOp (Rearrange perm stm_arr))) acc = distributeSingleUnaryStm acc stm stm_arr $ \nest outerpat arr -> do let r = length (snd nest) + 1@@ -596,17 +599,13 @@ arr_t <- lookupType arr pure $ stmsFromList- [ Let (Pat [PatElem arr' arr_t]) aux $ BasicOp $ Copy arr,+ [ Let (Pat [PatElem arr' arr_t]) aux $ BasicOp $ Replicate mempty $ Var arr, Let outerpat aux $ BasicOp $ Rearrange perm' arr' ] maybeDistributeStm stm@(Let _ aux (BasicOp (Reshape k reshape stm_arr))) acc = distributeSingleUnaryStm acc stm stm_arr $ \nest outerpat arr -> do let reshape' = Shape (kernelNestWidths nest) <> reshape pure $ oneStm $ Let outerpat aux $ BasicOp $ Reshape k reshape' arr-maybeDistributeStm stm@(Let _ aux (BasicOp (Rotate rots stm_arr))) acc =- distributeSingleUnaryStm acc stm stm_arr $ \nest outerpat arr -> do- let rots' = map (const $ intConst Int64 0) (kernelNestWidths nest) ++ rots- pure $ oneStm $ Let outerpat aux $ BasicOp $ Rotate rots' arr maybeDistributeStm stm@(Let pat aux (BasicOp (Update _ arr slice (Var v)))) acc | not $ null $ sliceDims slice = distributeSingleStm acc stm >>= \case
src/Futhark/Pass/ExtractKernels/Interchange.hs view
@@ -131,7 +131,10 @@ f (p, Var arg) | isMapInput arg, Array {} <- paramType p =- (p,) <$> letSubExp (baseString (paramName p) <> "_inter_copy") (BasicOp $ Copy arg)+ (p,)+ <$> letSubExp+ (baseString (paramName p) <> "_inter_copy")+ (BasicOp $ Replicate mempty $ Var arg) f (p, arg) = pure (p, arg)
src/Futhark/Pass/ExtractKernels/Intragroup.hs view
@@ -138,7 +138,7 @@ | Array {} <- kernelInputType inp = do v <- newVName $ baseString $ kernelInputName inp readKernelInput inp {kernelInputName = v}- letBindNames [kernelInputName inp] $ BasicOp $ Copy v+ letBindNames [kernelInputName inp] $ BasicOp $ Replicate mempty $ Var v | otherwise = readKernelInput inp
src/Futhark/Pass/KernelBabysitting.hs view
@@ -3,29 +3,14 @@ -- | Do various kernel optimisations - mostly related to coalescing. module Futhark.Pass.KernelBabysitting (babysitKernels) where -import Control.Arrow (first) import Control.Monad import Control.Monad.State.Strict+import Data.Bifunctor (first) import Data.Foldable-import Data.List (elemIndex, isPrefixOf, sort)+import Data.List qualified as L import Data.Map.Strict qualified as M import Data.Maybe-import Futhark.IR-import Futhark.IR.GPU hiding- ( BasicOp,- Body,- Exp,- FParam,- FunDef,- LParam,- Lambda,- Pat,- PatElem,- Prog,- RetType,- Stm,- )-import Futhark.MonadFreshNames+import Futhark.IR.GPU import Futhark.Pass import Futhark.Tools import Futhark.Util@@ -332,7 +317,7 @@ Nothing | any (`nameIn` free_ker_vars) (subExpVars is) = Nothing- | is `isPrefixOf` tgids =+ | is `L.isPrefixOf` tgids = Nothing | not (null tgids), not (null is),@@ -348,7 +333,7 @@ move is_rev (i, tgid) -- If tgid is in is_rev anywhere but at position i, and -- position i exists, we move it to position i instead.- | Just j <- elemIndex tgid is_rev,+ | Just j <- L.elemIndex tgid is_rev, i /= j, i < num_is = swap i j is_rev@@ -383,12 +368,12 @@ rearrangeInput (Just (Just current_perm)) perm arr | current_perm == perm = pure arr -- Already has desired representation. rearrangeInput Nothing perm arr- | sort perm == perm = pure arr -- We don't know the current+ | L.sort perm == perm = pure arr -- We don't know the current -- representation, but the indexing -- is linear, so let's hope the -- array is too. rearrangeInput (Just Just {}) perm arr- | sort perm == perm = rowMajorArray arr -- We just want a row-major array, no tricks.+ | L.sort perm == perm = rowMajorArray arr -- We just want a row-major array, no tricks. rearrangeInput manifest perm arr = do -- We may first manifest the array to ensure that it is flat in -- memory. This is sometimes unnecessary, in which case the copy
src/Futhark/Pass/LiftAllocations.hs view
@@ -1,10 +1,9 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TypeFamilies #-} --- | This pass attempts to lift allocations as far towards the top in their body--- as possible. It does not try to hoist allocations outside across body+-- | This pass attempts to lift allocations and asserts as far towards+-- the top in their body as possible. This helps memory short+-- circuiting do a better job, as it is sensitive to statement+-- ordering. It does not try to hoist allocations outside across body -- boundaries. module Futhark.Pass.LiftAllocations ( liftAllocationsSeqMem,@@ -15,49 +14,40 @@ import Control.Monad.Reader import Data.Sequence (Seq (..))+import Futhark.Analysis.Alias (aliasAnalysis)+import Futhark.IR.Aliases import Futhark.IR.GPUMem import Futhark.IR.MCMem import Futhark.IR.SeqMem import Futhark.Pass (Pass (..)) +liftInProg ::+ (AliasableRep rep, Mem rep inner, OpReturns (inner (Aliases rep))) =>+ (inner (Aliases rep) -> LiftM (inner (Aliases rep)) (inner (Aliases rep))) ->+ Prog rep ->+ Prog rep+liftInProg onOp prog =+ prog+ { progFuns = removeFunDefAliases . onFun <$> progFuns (aliasAnalysis prog)+ }+ where+ onFun f = f {funDefBody = onBody (funDefBody f)}+ onBody body = runReader (liftAllocationsInBody body) (Env onOp)+ liftAllocationsSeqMem :: Pass SeqMem SeqMem liftAllocationsSeqMem =- Pass "lift allocations" "lift allocations" $ \prog@Prog {progFuns} ->- pure $- prog- { progFuns =- fmap- ( \f@FunDef {funDefBody} ->- f {funDefBody = runReader (liftAllocationsInBody funDefBody) (Env pure)}- )- progFuns- }+ Pass "lift allocations" "lift allocations" $+ pure . liftInProg pure liftAllocationsGPUMem :: Pass GPUMem GPUMem liftAllocationsGPUMem =- Pass "lift allocations gpu" "lift allocations gpu" $ \prog@Prog {progFuns} ->- pure $- prog- { progFuns =- fmap- ( \f@FunDef {funDefBody} ->- f {funDefBody = runReader (liftAllocationsInBody funDefBody) (Env liftAllocationsInHostOp)}- )- progFuns- }+ Pass "lift allocations gpu" "lift allocations gpu" $+ pure . liftInProg liftAllocationsInHostOp liftAllocationsMCMem :: Pass MCMem MCMem liftAllocationsMCMem =- Pass "lift allocations mc" "lift allocations mc" $ \prog@Prog {progFuns} ->- pure $- prog- { progFuns =- fmap- ( \f@FunDef {funDefBody} ->- f {funDefBody = runReader (liftAllocationsInBody funDefBody) (Env liftAllocationsInMCOp)}- )- progFuns- }+ Pass "lift allocations mc" "lift allocations mc" $+ pure . liftInProg liftAllocationsInMCOp newtype Env inner = Env {onInner :: inner -> LiftM inner inner}@@ -65,69 +55,66 @@ type LiftM inner a = Reader (Env inner) a liftAllocationsInBody ::- (Mem rep inner, LetDec rep ~ LetDecMem) =>+ (Mem rep inner, Aliased rep) => Body rep -> LiftM (inner rep) (Body rep) liftAllocationsInBody body = do stms <- liftAllocationsInStms (bodyStms body) mempty mempty mempty pure $ body {bodyStms = stms} +liftInsideStm ::+ (Mem rep inner, Aliased rep) =>+ Stm rep ->+ LiftM (inner rep) (Stm rep)+liftInsideStm stm@(Let _ _ (Op (Inner inner))) = do+ on_inner <- asks onInner+ inner' <- on_inner inner+ pure $ stm {stmExp = Op $ Inner inner'}+liftInsideStm stm@(Let _ _ (Match cond_ses cases body dec)) = do+ cases' <- mapM (\(Case p b) -> Case p <$> liftAllocationsInBody b) cases+ body' <- liftAllocationsInBody body+ pure stm {stmExp = Match cond_ses cases' body' dec}+liftInsideStm stm@(Let _ _ (DoLoop params form body)) = do+ body' <- liftAllocationsInBody body+ pure stm {stmExp = DoLoop params form body'}+liftInsideStm stm = pure stm+ liftAllocationsInStms ::- (Mem rep inner, LetDec rep ~ LetDecMem) =>+ (Mem rep inner, Aliased rep) => -- | The input stms Stms rep -> -- | The lifted allocations and associated statements Stms rep -> -- | The other statements processed so far Stms rep ->- -- | Names we need to lift- Names ->+ -- | (Names we need to lift, consumed names)+ (Names, Names) -> LiftM (inner rep) (Stms rep) liftAllocationsInStms Empty lifted acc _ = pure $ lifted <> acc-liftAllocationsInStms (stms :|> stm@(Let (Pat [PatElem vname _]) _ (Op (Alloc _ _)))) lifted acc to_lift =- liftAllocationsInStms stms (stm :<| lifted) acc ((freeIn stm <> to_lift) `namesSubtract` oneName vname)-liftAllocationsInStms (stms :|> stm@(Let pat _ (Op (Inner inner)))) lifted acc to_lift = do- on_inner <- asks onInner- inner' <- on_inner inner- let stm' = stm {stmExp = Op $ Inner inner'}- pat_names = namesFromList $ patNames pat- if pat_names `namesIntersect` to_lift- then liftAllocationsInStms stms (stm' :<| lifted) acc ((to_lift `namesSubtract` pat_names) <> freeIn stm)- else liftAllocationsInStms stms lifted (stm' :<| acc) to_lift-liftAllocationsInStms (stms :|> stm@(Let pat aux (Match cond_ses cases body dec))) lifted acc to_lift = do- cases' <- mapM (\(Case p b) -> Case p <$> liftAllocationsInBody b) cases- body' <- liftAllocationsInBody body- let stm' = stm {stmExp = Match cond_ses cases' body' dec}- pat_names = namesFromList $ patNames pat- if pat_names `namesIntersect` to_lift- then- liftAllocationsInStms- stms- (stm' :<| lifted)- acc- ( (to_lift `namesSubtract` pat_names)- <> freeIn cond_ses- <> freeIn cases- <> freeIn body- <> freeIn dec- <> freeIn aux- )- else liftAllocationsInStms stms lifted (stm' :<| acc) to_lift-liftAllocationsInStms (stms :|> stm@(Let pat _ (DoLoop params form body))) lifted acc to_lift = do- body' <- liftAllocationsInBody body- let stm' = stm {stmExp = DoLoop params form body'}- pat_names = namesFromList $ patNames pat- if pat_names `namesIntersect` to_lift- then liftAllocationsInStms stms (stm' :<| lifted) acc ((to_lift `namesSubtract` pat_names) <> freeIn stm)- else liftAllocationsInStms stms lifted (stm' :<| acc) to_lift-liftAllocationsInStms (stms :|> stm@(Let pat _ _)) lifted acc to_lift = do- let pat_names = namesFromList (patNames pat)- if pat_names `namesIntersect` to_lift- then liftAllocationsInStms stms (stm :<| lifted) acc ((to_lift `namesSubtract` pat_names) <> freeIn stm)- else liftAllocationsInStms stms lifted (stm :<| acc) to_lift+liftAllocationsInStms (stms :|> stm) lifted acc (to_lift, consumed) = do+ stm' <- liftInsideStm stm+ case stmExp stm' of+ BasicOp Assert {} -> liftStm stm'+ Op Alloc {} -> liftStm stm'+ _ -> do+ let pat_names = namesFromList $ patNames $ stmPat stm'+ if (pat_names `namesIntersect` to_lift)+ || namesIntersect consumed (freeIn stm)+ then liftStm stm'+ else dontLiftStm stm'+ where+ liftStm stm' =+ liftAllocationsInStms stms (stm' :<| lifted) acc (to_lift', consumed')+ where+ to_lift' =+ freeIn stm'+ <> (to_lift `namesSubtract` namesFromList (patNames (stmPat stm')))+ consumed' = consumed <> consumedInStm stm'+ dontLiftStm stm' =+ liftAllocationsInStms stms lifted (stm' :<| acc) (to_lift, consumed) liftAllocationsInSegOp ::- (Mem rep inner, LetDec rep ~ LetDecMem) =>+ (Mem rep inner, Aliased rep) => SegOp lvl rep -> LiftM (inner rep) (SegOp lvl rep) liftAllocationsInSegOp (SegMap lvl sp tps body) = do@@ -143,11 +130,15 @@ stms <- liftAllocationsInStms (kernelBodyStms body) mempty mempty mempty pure $ SegHist lvl sp histops tps $ body {kernelBodyStms = stms} -liftAllocationsInHostOp :: HostOp NoOp GPUMem -> LiftM (HostOp NoOp GPUMem) (HostOp NoOp GPUMem)+liftAllocationsInHostOp ::+ HostOp NoOp (Aliases GPUMem) ->+ LiftM (HostOp NoOp (Aliases GPUMem)) (HostOp NoOp (Aliases GPUMem)) liftAllocationsInHostOp (SegOp op) = SegOp <$> liftAllocationsInSegOp op liftAllocationsInHostOp op = pure op -liftAllocationsInMCOp :: MCOp NoOp MCMem -> LiftM (MCOp NoOp MCMem) (MCOp NoOp MCMem)+liftAllocationsInMCOp ::+ MCOp NoOp (Aliases MCMem) ->+ LiftM (MCOp NoOp (Aliases MCMem)) (MCOp NoOp (Aliases MCMem)) liftAllocationsInMCOp (ParOp par op) = ParOp <$> traverse liftAllocationsInSegOp par <*> liftAllocationsInSegOp op liftAllocationsInMCOp op = pure op
src/Futhark/Pass/LowerAllocations.hs view
@@ -1,6 +1,3 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TypeFamilies #-} -- | This pass attempts to lower allocations as far towards the bottom of their@@ -22,44 +19,31 @@ import Futhark.IR.SeqMem import Futhark.Pass (Pass (..)) +lowerInProg ::+ (Mem rep inner, LetDec rep ~ LetDecMem) =>+ (inner rep -> LowerM (inner rep) (inner rep)) ->+ Prog rep ->+ Prog rep+lowerInProg onOp prog =+ prog {progFuns = fmap onFun (progFuns prog)}+ where+ onFun f = f {funDefBody = onBody (funDefBody f)}+ onBody body = runReader (lowerAllocationsInBody body) (Env onOp)+ lowerAllocationsSeqMem :: Pass SeqMem SeqMem lowerAllocationsSeqMem =- Pass "lower allocations" "lower allocations" $ \prog@Prog {progFuns} ->- pure $- prog- { progFuns =- fmap- ( \f@FunDef {funDefBody} ->- f {funDefBody = runReader (lowerAllocationsInBody funDefBody) (Env pure)}- )- progFuns- }+ Pass "lower allocations" "lower allocations" $+ pure . lowerInProg pure lowerAllocationsGPUMem :: Pass GPUMem GPUMem lowerAllocationsGPUMem =- Pass "lower allocations gpu" "lower allocations gpu" $ \prog@Prog {progFuns} ->- pure $- prog- { progFuns =- fmap- ( \f@FunDef {funDefBody} ->- f {funDefBody = runReader (lowerAllocationsInBody funDefBody) (Env lowerAllocationsInHostOp)}- )- progFuns- }+ Pass "lower allocations gpu" "lower allocations gpu" $+ pure . lowerInProg lowerAllocationsInHostOp lowerAllocationsMCMem :: Pass MCMem MCMem lowerAllocationsMCMem =- Pass "lower allocations mc" "lower allocations mc" $ \prog@Prog {progFuns} ->- pure $- prog- { progFuns =- fmap- ( \f@FunDef {funDefBody} ->- f {funDefBody = runReader (lowerAllocationsInBody funDefBody) (Env lowerAllocationsInMCOp)}- )- progFuns- }+ Pass "lower allocations mc" "lower allocations mc" $+ pure . lowerInProg lowerAllocationsInMCOp newtype Env inner = Env {onInner :: inner -> LowerM inner inner}
src/Futhark/Pkg/Types.hs view
@@ -45,7 +45,7 @@ import Data.Text qualified as T import Data.Text.IO qualified as T import Data.Traversable-import Data.Versions (SemVer (..), VUnit (..), prettySemVer)+import Data.Versions (Chunk (Alphanum), Release (Release), SemVer (..), prettySemVer) import Data.Void import System.FilePath import System.FilePath.Posix qualified as Posix@@ -68,13 +68,13 @@ -- @hash@ (typically the Git commit ID). This function detects such -- versions. isCommitVersion :: SemVer -> Maybe T.Text-isCommitVersion (SemVer 0 0 0 [_] (Just s)) = Just s+isCommitVersion (SemVer 0 0 0 (Just (Release (_ NE.:| []))) (Just s)) = Just s isCommitVersion _ = Nothing -- | @commitVersion timestamp commit@ constructs a commit version. commitVersion :: T.Text -> T.Text -> SemVer commitVersion time commit =- SemVer 0 0 0 [NE.singleton (Str time)] (Just commit)+ SemVer 0 0 0 (Just $ Release $ NE.singleton $ Alphanum time) (Just commit) -- | Unfortunately, Data.Versions has a buggy semver parser that -- collapses consecutive zeroes in the metadata field. So, we define@@ -90,8 +90,8 @@ minorP = majorP patchP = digitsP digitsP = read <$> ((T.unpack <$> string "0") <|> some digitChar)- preRel = maybe [] pure <$> optional preRel'- preRel' = char '-' *> (pure . Str . T.pack <$> some digitChar)+ preRel = fmap (Release . NE.singleton) <$> optional preRel'+ preRel' = char '-' *> (Alphanum . T.pack <$> some digitChar) metaData = optional metaData' metaData' = char '+' *> (T.pack <$> some alphaNumChar)
src/Futhark/Test.hs view
@@ -94,8 +94,8 @@ s <- BS.readFile file E.evaluate $ decompress s --- | Extract a prettyString representation of some 'Values'. In the IO--- monad because this might involve reading from a file. There is no+-- | Extract a text representation of some 'Values'. In the IO monad+-- because this might involve reading from a file. There is no -- guarantee that the resulting byte string yields a readable value. getValuesBS :: (MonadFail m, MonadIO m) => FutharkExe -> FilePath -> Values -> m BS.ByteString getValuesBS _ _ (Values vs) =
src/Futhark/Transform/FirstOrderTransform.hs view
@@ -178,12 +178,10 @@ letExp (baseString (paramName p)) . BasicOp $ Index arr $ fullSlice arr_t [DimFix $ Var i]- letBindNames [paramName p] $ BasicOp $ Copy p'+ letBindNames [paramName p] $ BasicOp $ Replicate mempty $ Var p' | otherwise ->- letBindNames [paramName p] $- BasicOp $- Index arr $- fullSlice arr_t [DimFix $ Var i]+ letBindNames [paramName p] . BasicOp . Index arr $+ fullSlice arr_t [DimFix $ Var i] -- Insert the statements of the lambda. We have taken care to -- ensure that the parameters are bound at this point.@@ -248,7 +246,8 @@ let copyIfArray se = do se_t <- subExpType se case (se_t, se) of- (Array {}, Var v) -> letSubExp (baseString v) $ BasicOp $ Copy v+ (Array {}, Var v) ->+ letSubExp (baseString v) $ BasicOp $ Replicate mempty se _ -> pure se nes' <- mapM copyIfArray nes
src/Futhark/Transform/Rename.hs view
@@ -17,6 +17,7 @@ renamePat, renameSomething, renameBound,+ renameStmsWith, -- * Renaming annotations RenameM,@@ -29,6 +30,7 @@ import Control.Monad.Reader import Control.Monad.State+import Data.Bitraversable import Data.Map.Strict qualified as M import Data.Maybe import Futhark.FreshNames hiding (newName)@@ -116,6 +118,17 @@ m a renameSomething = modifyNameSource . runRenamer . rename +-- | Rename statements, then rename something within the scope of+-- those statements.+renameStmsWith ::+ (MonadFreshNames m, Renameable rep, Rename a) =>+ Stms rep ->+ a ->+ m (Stms rep, a)+renameStmsWith stms a =+ modifyNameSource . runRenamer $ renamingStms stms $ \stms' ->+ (stms',) <$> rename a+ newtype RenameEnv = RenameEnv {envNameMap :: M.Map VName VName} -- | The monad in which renaming is performed.@@ -208,7 +221,7 @@ renameBound (map paramName params) $ do params' <- mapM rename params body' <- rename body- ret' <- rename ret+ ret' <- mapM (bitraverse rename pure) ret pure $ FunDef entry attrs fname ret' params' body' instance Rename SubExp where
src/Futhark/Util.hs view
@@ -13,6 +13,7 @@ maxinum, chunk, chunks,+ chunkLike, dropAt, takeLast, dropLast,@@ -48,21 +49,23 @@ traverseFold, fixPoint, concatMapM,+ topologicalSort, ) where -import Control.Arrow (first) import Control.Concurrent import Control.Exception import Control.Monad import Control.Monad.State import Crypto.Hash.MD5 as MD5+import Data.Bifunctor import Data.ByteString qualified as BS import Data.ByteString.Base16 qualified as Base16 import Data.Char import Data.Either import Data.Foldable (fold, toList) import Data.Function ((&))+import Data.IntMap qualified as IM import Data.List (findIndex, foldl', genericDrop, genericSplitAt, sortBy) import Data.List.NonEmpty qualified as NE import Data.Map qualified as M@@ -131,6 +134,12 @@ let (bef, aft) = splitAt n xs in bef : chunks ns aft +-- | @chunkLike xss ys@ chunks the elements of @ys@ to match the+-- elements of @xss@. The sum of the lengths of the sublists of @xss@+-- must match the length of @ys@.+chunkLike :: [[a]] -> [b] -> [[b]]+chunkLike as = chunks (map length as)+ -- | Like 'maximum', but returns zero for an empty list. maxinum :: (Num a, Ord a, Foldable f) => f a -> a maxinum = foldl' max 0@@ -461,3 +470,31 @@ concatMapM :: (Monad m, Monoid b) => (a -> m b) -> [a] -> m b concatMapM f xs = mconcat <$> mapM f xs++-- | Topological sorting of an array with an adjancency function,+-- if there is a cycle, it cause an error+-- @a `dep` b@ means 'a -> b', and the returned array guarantee that for i < j,+-- @not ( (ret !! j) `dep` (ret !! i) )@.+topologicalSort :: (a -> a -> Bool) -> [a] -> [a]+topologicalSort dep nodes =+ fst $ execState (mapM_ (sorting . snd) nodes_idx) (mempty, mempty)+ where+ nodes_idx = zip nodes [0 ..]+ depends_of a (b, i) =+ if a `dep` b+ then Just i+ else Nothing++ -- Using an IntMap Bool+ -- when reading a lookup:+ -- \* Nothing : never explored+ -- \* Just True : being explored+ -- \* Just False : explored+ sorting i = do+ status <- gets $ IM.lookup i . snd+ when (status == Just True) $ error "topological sorting has encountered a cycle"+ unless (status == Just False) $ do+ let node = nodes !! i+ modify $ second $ IM.insert i True+ mapM_ sorting $ mapMaybe (depends_of node) nodes_idx+ modify $ bimap (node :) (IM.insert i False)
src/Futhark/Util/Pretty.hs view
@@ -7,6 +7,7 @@ prettyTuple, prettyTupleLines, prettyString,+ prettyStringOneLine, prettyText, prettyTextOneLine, docText,@@ -49,7 +50,7 @@ import Prettyprinter.Render.Terminal qualified import Prettyprinter.Render.Text qualified import Prettyprinter.Symbols.Ascii-import System.IO (Handle, hIsTerminalDevice, stdout)+import System.IO (Handle, hIsTerminalDevice, hPutStrLn, stdout) -- | Print a doc with styling to the given file; stripping colors if -- the file does not seem to support such things.@@ -67,7 +68,7 @@ hPutDocLn :: Handle -> Doc AnsiStyle -> IO () hPutDocLn h d = do hPutDoc h d- putStrLn ""+ hPutStrLn h "" -- | Like 'hPutDoc', but to stdout. putDoc :: Doc AnsiStyle -> IO ()@@ -75,8 +76,8 @@ -- | Like 'putDoc', but with a final newline. putDocLn :: Doc AnsiStyle -> IO ()-putDocLn h = do- putDoc h+putDocLn d = do+ putDoc d putStrLn "" -- | Produce text suitable for printing on the given handle. This@@ -94,6 +95,10 @@ -- | Prettyprint a value to a 'String', appropriately wrapped. prettyString :: Pretty a => a -> String prettyString = T.unpack . prettyText++-- | Prettyprint a value to a 'String' on a single line.+prettyStringOneLine :: Pretty a => a -> String+prettyStringOneLine = T.unpack . prettyTextOneLine -- | Prettyprint a value to a 'Text', appropriately wrapped. prettyText :: Pretty a => a -> Text
src/Language/Futhark.hs view
@@ -4,26 +4,6 @@ module Language.Futhark.Prop, module Language.Futhark.FreeVars, module Language.Futhark.Pretty,- Ident,- DimIndex,- Slice,- AppExp,- Exp,- Pat,- ModExp,- ModParam,- SigExp,- ModBind,- SigBind,- ValBind,- Dec,- Spec,- Prog,- TypeBind,- StructTypeArg,- ScalarType,- TypeParam,- Case, ) where @@ -31,63 +11,3 @@ import Language.Futhark.Pretty import Language.Futhark.Prop import Language.Futhark.Syntax---- | An identifier with type- and aliasing information.-type Ident = IdentBase Info VName---- | An index with type information.-type DimIndex = DimIndexBase Info VName---- | A slice with type information.-type Slice = SliceBase Info VName---- | An expression with type information.-type Exp = ExpBase Info VName---- | An application expression with type information.-type AppExp = AppExpBase Info VName---- | A pattern with type information.-type Pat = PatBase Info VName---- | An constant declaration with type information.-type ValBind = ValBindBase Info VName---- | A type binding with type information.-type TypeBind = TypeBindBase Info VName---- | A type-checked module binding.-type ModBind = ModBindBase Info VName---- | A type-checked module type binding.-type SigBind = SigBindBase Info VName---- | A type-checked module expression.-type ModExp = ModExpBase Info VName---- | A type-checked module parameter.-type ModParam = ModParamBase Info VName---- | A type-checked module type expression.-type SigExp = SigExpBase Info VName---- | A type-checked declaration.-type Dec = DecBase Info VName---- | A type-checked specification.-type Spec = SpecBase Info VName---- | An Futhark program with type information.-type Prog = ProgBase Info VName---- | A known type arg with shape annotations.-type StructTypeArg = TypeArg Size---- | A type-checked type parameter.-type TypeParam = TypeParamBase VName---- | A known scalar type with no shape annotations.-type ScalarType = ScalarTypeBase ()---- | A type-checked case (of a match expression).-type Case = CaseBase Info VName
src/Language/Futhark/Core.hs view
@@ -3,6 +3,7 @@ -- representation. module Language.Futhark.Core ( Uniqueness (..),+ NoUniqueness (..), -- * Location utilities SrcLoc,@@ -70,6 +71,20 @@ instance Pretty Uniqueness where pretty Unique = "*" pretty Nonunique = mempty++-- | A fancier name for @()@ - encodes no uniqueness information.+-- Also has a different prettyprinting instance.+data NoUniqueness = NoUniqueness+ deriving (Eq, Ord, Show)++instance Semigroup NoUniqueness where+ NoUniqueness <> NoUniqueness = NoUniqueness++instance Monoid NoUniqueness where+ mempty = NoUniqueness++instance Pretty NoUniqueness where+ pretty _ = mempty -- | The abstract (not really) type representing names in the Futhark -- compiler. 'String's, being lists of characters, are very slow,
src/Language/Futhark/FreeVars.hs view
@@ -5,40 +5,39 @@ freeInPat, freeInType, freeWithout,- FV (..),+ FV,+ fvVars, ) where -import Data.Map.Strict qualified as M import Data.Set qualified as S import Language.Futhark.Prop import Language.Futhark.Syntax --- | A set of names where we also track their type.-newtype FV = FV {unFV :: M.Map VName StructType}+-- | A set of names.+newtype FV = FV {unFV :: S.Set VName} deriving (Show) +-- | The set of names in an 'FV'.+fvVars :: FV -> S.Set VName+fvVars = unFV+ instance Semigroup FV where- FV x <> FV y = FV $ M.unionWith max x y+ FV x <> FV y = FV $ x <> y instance Monoid FV where mempty = FV mempty -- | Set subtraction. Do not consider those variables as free. freeWithout :: FV -> S.Set VName -> FV-freeWithout (FV x) y = FV $ M.filterWithKey keep x- where- keep k _ = k `S.notMember` y--ident :: IdentBase Info VName -> FV-ident v = FV $ M.singleton (identName v) (toStruct $ unInfo (identType v))+freeWithout (FV x) y = FV $ x `S.difference` y -size :: VName -> FV-size v = FV $ M.singleton v $ Scalar $ Prim $ Signed Int64+-- | As 'freeWithout', but for lists.+freeWithoutL :: FV -> [VName] -> FV+freeWithoutL fv y = fv `freeWithout` S.fromList y --- | A 'FV' with these names, considered to be sizes.-sizes :: S.Set VName -> FV-sizes = foldMap size+ident :: Ident t -> FV+ident = FV . S.singleton . identName -- | Compute the set of free variables of an expression. freeInExp :: ExpBase Info VName -> FV@@ -56,32 +55,30 @@ freeInExpField (RecordFieldExplicit _ e _) = freeInExp e freeInExpField (RecordFieldImplicit vn t _) = ident $ Ident vn t mempty ArrayLit es t _ ->- foldMap freeInExp es <> sizes (freeInType $ unInfo t)+ foldMap freeInExp es <> freeInType (unInfo t) AppExp (Range e me incl _) _ -> freeInExp e <> foldMap freeInExp me <> foldMap freeInExp incl- Var qn (Info t) _ -> FV $ M.singleton (qualLeaf qn) $ toStruct t+ Var qn _ _ -> FV $ S.singleton $ qualLeaf qn Ascript e _ _ -> freeInExp e- AppExp (Coerce e _ _) (Info ar) ->- freeInExp e <> sizes (freeInType (appResType ar))+ Coerce e _ (Info t) _ ->+ freeInExp e <> freeInType t AppExp (LetPat let_sizes pat e1 e2 _) _ -> freeInExp e1- <> ( (sizes (freeInPat pat) <> freeInExp e2)- `freeWithout` (patNames pat <> S.fromList (map sizeName let_sizes))+ <> ( (freeInPat pat <> freeInExp e2)+ `freeWithoutL` (patNames pat <> map sizeName let_sizes) ) AppExp (LetFun vn (tparams, pats, _, _, e1) e2 _) _ ->- ( (freeInExp e1 <> sizes (foldMap freeInPat pats))- `freeWithout` ( foldMap patNames pats- <> S.fromList (map typeParamName tparams)- )+ ( (freeInExp e1 <> foldMap freeInPat pats)+ `freeWithoutL` (foldMap patNames pats <> map typeParamName tparams) ) <> (freeInExp e2 `freeWithout` S.singleton vn) AppExp (If e1 e2 e3 _) _ -> freeInExp e1 <> freeInExp e2 <> freeInExp e3 AppExp (Apply f args _) _ -> freeInExp f <> foldMap (freeInExp . snd) args Negate e _ -> freeInExp e Not e _ -> freeInExp e- Lambda pats e0 _ (Info (_, RetType dims t)) _ ->- (sizes (foldMap freeInPat pats) <> freeInExp e0 <> sizes (freeInType t))- `freeWithout` (foldMap patNames pats <> S.fromList dims)+ Lambda pats e0 _ (Info (RetType dims t)) _ ->+ (foldMap freeInPat pats <> freeInExp e0 <> freeInType t)+ `freeWithoutL` (foldMap patNames pats <> dims) OpSection {} -> mempty OpSectionLeft _ _ e _ _ _ -> freeInExp e OpSectionRight _ _ e _ _ _ -> freeInExp e@@ -91,14 +88,14 @@ let (e2fv, e2ident) = formVars form in freeInExp e1 <> ( (e2fv <> freeInExp e3)- `freeWithout` (S.fromList sparams <> patNames pat <> e2ident)+ `freeWithoutL` (sparams <> patNames pat <> e2ident) ) where- formVars (For v e2) = (freeInExp e2, S.singleton $ identName v)+ formVars (For v e2) = (freeInExp e2, [identName v]) formVars (ForIn p e2) = (freeInExp e2, patNames p) formVars (While e2) = (freeInExp e2, mempty)- AppExp (BinOp (qn, _) (Info qn_t) (e1, _) (e2, _) _) _ ->- FV (M.singleton (qualLeaf qn) $ toStruct qn_t)+ AppExp (BinOp (qn, _) _ (e1, _) (e2, _) _) _ ->+ FV (S.singleton (qualLeaf qn)) <> freeInExp e1 <> freeInExp e2 Project _ e _ _ -> freeInExp e@@ -116,8 +113,8 @@ AppExp (Match e cs _) _ -> freeInExp e <> foldMap caseFV cs where caseFV (CasePat p eCase _) =- (sizes (freeInPat p) <> freeInExp eCase)- `freeWithout` patNames p+ (freeInPat p <> freeInExp eCase)+ `freeWithoutL` patNames p freeInDimIndex :: DimIndexBase Info VName -> FV freeInDimIndex (DimFix e) = freeInExp e@@ -125,23 +122,15 @@ foldMap (foldMap freeInExp) [me1, me2, me3] -- | Free variables in pattern (including types of the bound identifiers).-freeInPat :: PatBase Info VName -> S.Set VName-freeInPat (TuplePat ps _) = foldMap freeInPat ps-freeInPat (RecordPat fs _) = foldMap (freeInPat . snd) fs-freeInPat (PatParens p _) = freeInPat p-freeInPat (Id _ (Info tp) _) = freeInType tp-freeInPat (Wildcard (Info tp) _) = freeInType tp-freeInPat (PatAscription p _ _) = freeInPat p-freeInPat (PatLit _ (Info tp) _) = freeInType tp-freeInPat (PatConstr _ _ ps _) = foldMap freeInPat ps-freeInPat (PatAttr _ p _) = freeInPat p+freeInPat :: Pat (TypeBase Size u) -> FV+freeInPat = foldMap freeInType -- | Free variables in the type (meaning those that are used in size expression).-freeInType :: TypeBase Size as -> S.Set VName+freeInType :: TypeBase Size u -> FV freeInType t = case t of- Array _ _ s a ->- freeInType (Scalar a) <> foldMap onSize (shapeDims s)+ Array _ s a ->+ freeInType (Scalar a) <> foldMap freeInExp (shapeDims s) Scalar (Record fs) -> foldMap freeInType fs Scalar Prim {} ->@@ -149,15 +138,13 @@ Scalar (Sum cs) -> foldMap (foldMap freeInType) cs Scalar (Arrow _ v _ t1 (RetType dims t2)) ->- S.filter (notV v) $ S.filter (`notElem` dims) $ freeInType t1 <> freeInType t2- Scalar (TypeVar _ _ _ targs) ->+ FV . S.filter (\k -> notV v k && notElem k dims) $+ unFV (freeInType t1 <> freeInType t2)+ Scalar (TypeVar _ _ targs) -> foldMap typeArgDims targs where- typeArgDims (TypeArgDim d _) = onSize d- typeArgDims (TypeArgType at _) = freeInType at+ typeArgDims (TypeArgDim d) = freeInExp d+ typeArgDims (TypeArgType at) = freeInType at notV Unnamed = const True notV (Named v) = (/= v)-- onSize (NamedSize qn) = S.singleton $ qualLeaf qn- onSize _ = mempty
src/Language/Futhark/Interpreter.hs view
@@ -33,7 +33,7 @@ import Control.Monad.State import Control.Monad.Trans.Maybe import Data.Array-import Data.Bifunctor (first, second)+import Data.Bifunctor import Data.List ( find, foldl',@@ -42,13 +42,16 @@ isPrefixOf, transpose, )+import Data.List qualified as L import Data.List.NonEmpty qualified as NE import Data.Map qualified as M import Data.Maybe import Data.Monoid hiding (Sum)+import Data.Ord+import Data.Set qualified as S import Data.Text qualified as T import Futhark.Data qualified as V-import Futhark.Util (chunk, maybeHead, splitFromEnd)+import Futhark.Util (chunk, maybeHead) import Futhark.Util.Loc import Futhark.Util.Pretty hiding (apply) import Language.Futhark hiding (Shape, matchDims)@@ -146,38 +149,67 @@ extSizeEnv = i64Env <$> getSizes valueStructType :: ValueType -> StructType-valueStructType = first (ConstSize . fromIntegral)+valueStructType = first $ flip sizeFromInteger mempty . toInteger -resolveTypeParams :: [VName] -> StructType -> StructType -> Env-resolveTypeParams names = match+resolveTypeParams ::+ [VName] ->+ StructType ->+ StructType ->+ ([(VName, ([VName], StructType))], [(VName, Exp)])+resolveTypeParams names orig_t1 orig_t2 =+ execState (match mempty orig_t1 orig_t2) mempty where- match (Scalar (TypeVar _ _ tn _)) t- | qualLeaf tn `elem` names =- typeEnv $ M.singleton (qualLeaf tn) t- match (Scalar (Record poly_fields)) (Scalar (Record fields)) =- mconcat $- M.elems $- M.intersectionWith match poly_fields fields- match (Scalar (Sum poly_fields)) (Scalar (Sum fields)) =- mconcat $- map mconcat $- M.elems $- M.intersectionWith (zipWith match) poly_fields fields+ addType v t = modify $ first $ L.insertBy (comparing fst) (v, t)+ addDim v e = modify $ second $ L.insertBy (comparing fst) (v, e)++ match bound (Scalar (TypeVar _ tn _)) t+ | qualLeaf tn `elem` names = addType (qualLeaf tn) (bound, t)+ match bound (Scalar (Record poly_fields)) (Scalar (Record fields)) =+ sequence_ . M.elems $+ M.intersectionWith (match bound) poly_fields fields+ match bound (Scalar (Sum poly_fields)) (Scalar (Sum fields)) =+ sequence_ . mconcat . M.elems $+ M.intersectionWith (zipWith $ match bound) poly_fields fields match- (Scalar (Arrow _ _ _ poly_t1 (RetType _ poly_t2)))- (Scalar (Arrow _ _ _ t1 (RetType _ t2))) =- match poly_t1 t1 <> match poly_t2 t2- match poly_t t+ bound+ (Scalar (Arrow _ p1 _ poly_t1 (RetType dims1 poly_t2)))+ (Scalar (Arrow _ p2 _ t1 (RetType dims2 t2))) = do+ let bound' = mapMaybe paramName [p1, p2] <> dims1 <> dims2 <> bound+ match bound' poly_t1 t1+ match bound' (toStruct poly_t2) (toStruct t2)+ match bound poly_t t | d1 : _ <- shapeDims (arrayShape poly_t),- d2 : _ <- shapeDims (arrayShape t) =- matchDims d1 d2 <> match (stripArray 1 poly_t) (stripArray 1 t)- match _ _ = mempty+ d2 : _ <- shapeDims (arrayShape t) = do+ matchDims bound d1 d2+ match bound (stripArray 1 poly_t) (stripArray 1 t)+ match bound t1 t2+ | Just t1' <- isAccType t1,+ Just t2' <- isAccType t2 =+ match bound t1' t2'+ match _ _ _ = pure mempty - matchDims (NamedSize (QualName _ d1)) (ConstSize d2)- | d1 `elem` names =- i64Env $ M.singleton d1 $ fromIntegral d2- matchDims _ _ = mempty+ matchDims bound e1 e2+ | e1 == anySize || e2 == anySize = pure mempty+ | otherwise = matchExps bound e1 e2 + matchExps bound (Var (QualName _ d1) _ _) e+ | d1 `elem` names,+ not $ any (`elem` bound) $ fvVars $ freeInExp e =+ addDim d1 e+ matchExps bound e1 e2+ | Just es <- similarExps e1 e2 =+ mapM_ (uncurry $ matchExps bound) es+ matchExps _ _ _ = pure mempty++evalResolved ::+ Eval ->+ ([(VName, ([VName], StructType))], [(VName, Exp)]) ->+ EvalM Env+evalResolved eval' (ts, ds) = do+ ts' <- mapM (traverse $ \(bound, t) -> evalType eval' (S.fromList bound) t) ts+ ds' <- mapM (traverse $ fmap asInt64 . eval') ds+ pure $ typeEnv (M.fromList ts') <> i64Env (M.fromList ds')+ resolveExistentials :: [VName] -> StructType -> ValueShape -> M.Map VName Int64 resolveExistentials names = match where@@ -195,7 +227,7 @@ matchDims d1 d2 <> match (stripArray 1 poly_t) rowshape match _ _ = mempty - matchDims (NamedSize (QualName _ d1)) d2+ matchDims (Var (QualName _ d1) _ _) d2 | d1 `elem` names = M.singleton d1 d2 matchDims _ _ = mempty @@ -261,11 +293,16 @@ lookupType :: QualName VName -> Env -> Maybe T.TypeBinding lookupType = lookupInEnv envType +-- | An expression evaluator that embeds an environment.+type Eval = Exp -> EvalM Value+ -- | A TermValue with a 'Nothing' type annotation is an intrinsic. data TermBinding = TermValue (Maybe T.BoundV) Value- | -- | A polymorphic value that must be instantiated.- TermPoly (Maybe T.BoundV) (StructType -> EvalM Value)+ | -- | A polymorphic value that must be instantiated. The+ -- 'StructType' provided is un-evaluated, but parts of it can be+ -- evaluated using the provided 'Eval' function.+ TermPoly (Maybe T.BoundV) (StructType -> Eval -> EvalM Value) | TermModule Module data Module@@ -370,14 +407,14 @@ f' <- apply noLoc mempty f x apply noLoc mempty f' y -matchPat :: Env -> Pat -> Value -> EvalM Env+matchPat :: Env -> Pat (TypeBase Size u) -> Value -> EvalM Env matchPat env p v = do m <- runMaybeT $ patternMatch env p v case m of- Nothing -> error $ "matchPat: missing case for " <> prettyString p ++ " and " <> show v+ Nothing -> error $ "matchPat: missing case for " <> prettyString (toStruct <$> p) ++ " and " <> show v Just env' -> pure env' -patternMatch :: Env -> Pat -> Value -> MaybeT EvalM Env+patternMatch :: Env -> Pat (TypeBase Size u) -> Value -> MaybeT EvalM Env patternMatch env (Id v (Info t) _) val = lift $ pure $@@ -395,8 +432,8 @@ patternMatch env p v patternMatch env (PatLit l t _) v = do l' <- case l of- PatLitInt x -> lift $ eval env $ IntLit x t mempty- PatLitFloat x -> lift $ eval env $ FloatLit x t mempty+ PatLitInt x -> lift $ eval env $ IntLit x (toStruct <$> t) mempty+ PatLitFloat x -> lift $ eval env $ FloatLit x (toStruct <$> t) mempty PatLitPrim lv -> pure $ ValuePrim lv if v == l' then pure env@@ -547,105 +584,131 @@ -- | Expand type based on information that was not available at -- type-checking time (the structure of abstract types).-expandType :: Env -> StructType -> StructType+expandType :: Env -> TypeBase Size u -> TypeBase Size u expandType _ (Scalar (Prim pt)) = Scalar $ Prim pt expandType env (Scalar (Record fs)) = Scalar $ Record $ fmap (expandType env) fs-expandType env (Scalar (Arrow () p d t1 (RetType dims t2))) =- Scalar $ Arrow () p d (expandType env t1) (RetType dims (expandType env t2))-expandType env t@(Array _ u shape _) =+expandType env (Scalar (Arrow u p d t1 (RetType dims t2))) =+ Scalar $ Arrow u p d (expandType env t1) (RetType dims (expandType env t2))+expandType env t@(Array u shape _) = let et = stripArray (shapeRank shape) t et' = expandType env et- in arrayOf u shape et'-expandType env t@(Scalar (TypeVar () _ tn args)) =+ in second (const u) (arrayOf shape $ toStruct et')+expandType env (Scalar (TypeVar u tn args)) = case lookupType tn env of- Just (T.TypeAbbr _ ps (RetType _ t')) ->+ Just (T.TypeAbbr _ ps (RetType ext t')) -> let (substs, types) = mconcat $ zipWith matchPtoA ps args- onDim (NamedSize v) = fromMaybe (NamedSize v) $ M.lookup (qualLeaf v) substs+ onDim (Var v _ _)+ | Just e <- M.lookup (qualLeaf v) substs =+ e+ -- The next case can occur when a type with existential size+ -- has been hidden by a module ascription,+ -- e.g. tests/modules/sizeparams4.fut.+ onDim e+ | any (`elem` ext) $ fvVars $ freeInExp e = anySize onDim d = d in if null ps- then first onDim t'- else expandType (Env mempty types <> env) $ first onDim t'- Nothing -> t+ then bimap onDim (const u) t'+ else expandType (Env mempty types <> env) $ bimap onDim (const u) t'+ Nothing ->+ -- This case only happens for built-in abstract types,+ -- e.g. accumulators.+ Scalar (TypeVar u tn $ map expandArg args) where- matchPtoA (TypeParamDim p _) (TypeArgDim (NamedSize qv) _) =- (M.singleton p $ NamedSize qv, mempty)- matchPtoA (TypeParamDim p _) (TypeArgDim (ConstSize k) _) =- (M.singleton p $ ConstSize k, mempty)- matchPtoA (TypeParamType l p _) (TypeArgType t' _) =+ matchPtoA (TypeParamDim p _) (TypeArgDim e) =+ (M.singleton p e, mempty)+ matchPtoA (TypeParamType l p _) (TypeArgType t') = let t'' = expandType env t' in (mempty, M.singleton p $ T.TypeAbbr l [] $ RetType [] t'') matchPtoA _ _ = mempty+ expandArg (TypeArgDim s) = TypeArgDim s+ expandArg (TypeArgType t) = TypeArgType $ expandType env t expandType env (Scalar (Sum cs)) = Scalar $ Sum $ (fmap . fmap) (expandType env) cs --- | First expand type abbreviations, then evaluate all possible--- sizes.-evalType :: Env -> StructType -> EvalM StructType-evalType outer_env t = do+evalWithExts :: Env -> EvalM Eval+evalWithExts env = do size_env <- extSizeEnv- let env = size_env <> outer_env- evalDim (NamedSize qn)- | Just (TermValue _ (ValuePrim (SignedValue (Int64Value x)))) <-- lookupVar qn env =- ConstSize $ fromIntegral x- evalDim d = d- pure $ first evalDim $ expandType env t+ pure $ eval $ size_env <> env +-- | Evaluate all possible sizes, except those that contain free+-- variables in the set of names.+evalType :: Eval -> S.Set VName -> StructType -> EvalM StructType+evalType eval' outer_bound t = do+ let evalDim bound _ e+ | canBeEvaluated bound e = do+ x <- asInteger <$> eval' e+ pure $ sizeFromInteger x mempty+ evalDim _ _ d = pure d+ traverseDims evalDim t+ where+ canBeEvaluated bound e =+ let free = fvVars $ freeInExp e+ in not $ any (`S.member` bound) free || any (`S.member` outer_bound) free+ evalTermVar :: Env -> QualName VName -> StructType -> EvalM Value evalTermVar env qv t = case lookupVar qv env of- Just (TermPoly _ v) -> v =<< evalType env t+ Just (TermPoly _ v) -> v (expandType env t) =<< evalWithExts env Just (TermValue _ v) -> pure v- _ -> error $ "\"" <> prettyString qv <> "\" is not bound to a value."+ _ -> do+ ss <- map (locText . srclocOf) <$> stacktrace+ error $+ prettyString qv+ <> " is not bound to a value.\n"+ <> T.unpack (prettyStacktrace 0 ss) typeValueShape :: Env -> StructType -> EvalM ValueShape typeValueShape env t = do- t' <- evalType env t+ eval' <- evalWithExts env+ t' <- evalType eval' mempty $ expandType env t case traverse dim $ typeShape t' of Nothing -> error $ "typeValueShape: failed to fully evaluate type " <> prettyString t' Just shape -> pure shape where- dim (ConstSize x) = Just $ fromIntegral x+ dim (IntLit x _ _) = Just $ fromIntegral x dim _ = Nothing -evalFunction :: Env -> [VName] -> [Pat] -> Exp -> StructType -> EvalM Value+-- Sometimes type instantiation is not quite enough - then we connect+-- up the missing sizes here. In particular used for eta-expanded+-- entry points.+linkMissingSizes :: [VName] -> Pat (TypeBase Size u) -> Value -> Env -> Env+linkMissingSizes [] _ _ env = env+linkMissingSizes missing_sizes p v env =+ env <> i64Env (resolveExistentials missing_sizes p_t (valueShape v))+ where+ p_t = expandType env $ patternStructType p++evalFunction :: Env -> [VName] -> [Pat ParamType] -> Exp -> ResType -> EvalM Value -- We treat zero-parameter lambdas as simply an expression to -- evaluate immediately. Note that this is *not* the same as a lambda -- that takes an empty tuple '()' as argument! Zero-parameter lambdas -- can never occur in a well-formed Futhark program, but they are -- convenient in the interpreter.-evalFunction env _ [] body rettype =+evalFunction env missing_sizes [] body rettype = -- Eta-expand the rest to make any sizes visible. etaExpand [] env rettype where- etaExpand vs env' (Scalar (Arrow _ _ _ pt (RetType _ rt))) =+ etaExpand vs env' (Scalar (Arrow _ _ _ p_t (RetType _ rt))) = do pure . ValueFun $ \v -> do- env'' <- matchPat env' (Wildcard (Info $ fromStruct pt) noLoc) v+ let p = Wildcard (Info p_t) noLoc+ env'' <- linkMissingSizes missing_sizes p v <$> matchPat env' p v etaExpand (v : vs) env'' rt etaExpand vs env' _ = do f <- localExts $ eval env' body foldM (apply noLoc mempty) f $ reverse vs evalFunction env missing_sizes (p : ps) body rettype = pure . ValueFun $ \v -> do- env' <- matchPat env p v- -- Fix up the last sizes, if any.- let p_t = expandType env $ patternStructType p- env''- | null missing_sizes =- env'- | otherwise =- env' <> i64Env (resolveExistentials missing_sizes p_t (valueShape v))- evalFunction env'' missing_sizes ps body rettype+ env' <- linkMissingSizes missing_sizes p v <$> matchPat env p v+ evalFunction env' missing_sizes ps body rettype evalFunctionBinding :: Env -> [TypeParam] ->- [Pat] ->- StructRetType ->+ [Pat ParamType] ->+ ResRetType -> Exp -> EvalM TermBinding evalFunctionBinding env tparams ps ret fbody = do- let arrow (xp, d, xt) yt = Scalar $ Arrow () xp d xt $ RetType [] yt- ftype = foldr (arrow . patternParam) (retType ret) ps+ let ftype = funType ps ret retext = case ps of [] -> retDims ret _ -> []@@ -656,19 +719,20 @@ fmap (TermValue (Just $ T.BoundV [] ftype)) . returned env (retType ret) retext =<< evalFunction env [] ps fbody (retType ret)- else pure . TermPoly (Just $ T.BoundV [] ftype) $ \ftype' -> do- let tparam_names = map typeParamName tparams- env' = resolveTypeParams tparam_names ftype ftype' <> env-- -- In some cases (abstract lifted types) there may be- -- missing sizes that were not fixed by the type- -- instantiation. These will have to be set by looking- -- at the actual function arguments.- missing_sizes =- filter (`M.notMember` envTerm env') $- map typeParamName (filter isSizeParam tparams)- returned env (retType ret) retext- =<< evalFunction env' missing_sizes ps fbody (retType ret)+ else pure . TermPoly (Just $ T.BoundV [] ftype) $ \ftype' ->+ let resolved = resolveTypeParams (map typeParamName tparams) ftype ftype'+ in \eval' -> do+ tparam_env <- evalResolved eval' resolved+ let env' = tparam_env <> env+ -- In some cases (abstract lifted types) there may be+ -- missing sizes that were not fixed by the type+ -- instantiation. These will have to be set by looking+ -- at the actual function arguments.+ missing_sizes =+ filter (`M.notMember` envTerm env') $+ map typeParamName (filter isSizeParam tparams)+ returned env (retType ret) retext+ =<< evalFunction env' missing_sizes ps fbody (retType ret) evalArg :: Env -> Exp -> Maybe VName -> EvalM Value evalArg env e ext = do@@ -686,8 +750,8 @@ valueShape v pure v -evalAppExp :: Env -> StructType -> AppExp -> EvalM Value-evalAppExp env _ (Range start maybe_second end loc) = do+evalAppExp :: Env -> AppExp -> EvalM Value+evalAppExp env (Range start maybe_second end loc) = do start' <- asInteger <$> eval env start maybe_second' <- traverse (fmap asInteger . eval env) maybe_second end' <- traverse (fmap asInteger . eval env) end@@ -735,54 +799,36 @@ UpToExclusive x -> "..<" <> prettyText x ) <> " is invalid."-evalAppExp env t (Coerce e te loc) = do- v <- eval env e- case checkShape (structTypeShape t) (valueShape v) of- Just _ -> pure v- Nothing ->- bad loc env . docText $- "Value `"- <> prettyValue v- <> "` of shape `"- <> pretty (valueShape v)- <> "` cannot match shape of type `"- <> pretty te- <> "` (`"- <> pretty t- <> "`)"-evalAppExp env _ (LetPat sizes p e body _) = do+evalAppExp env (LetPat sizes p e body _) = do v <- eval env e env' <- matchPat env p v let p_t = expandType env $ patternStructType p v_s = valueShape v env'' = env' <> i64Env (resolveExistentials (map sizeName sizes) p_t v_s) eval env'' body-evalAppExp env _ (LetFun f (tparams, ps, _, Info ret, fbody) body _) = do+evalAppExp env (LetFun f (tparams, ps, _, Info ret, fbody) body _) = do binding <- evalFunctionBinding env tparams ps ret fbody eval (env {envTerm = M.insert f binding $ envTerm env}) body-evalAppExp- env- _- (BinOp (op, _) op_t (x, Info (_, xext)) (y, Info (_, yext)) loc)- | baseString (qualLeaf op) == "&&" = do- x' <- asBool <$> eval env x- if x'- then eval env y- else pure $ ValuePrim $ BoolValue False- | baseString (qualLeaf op) == "||" = do- x' <- asBool <$> eval env x- if x'- then pure $ ValuePrim $ BoolValue True- else eval env y- | otherwise = do- op' <- eval env $ Var op op_t loc- x' <- evalArg env x xext- y' <- evalArg env y yext- apply2 loc env op' x' y'-evalAppExp env _ (If cond e1 e2 _) = do+evalAppExp env (BinOp (op, _) op_t (x, Info xext) (y, Info yext) loc)+ | baseString (qualLeaf op) == "&&" = do+ x' <- asBool <$> eval env x+ if x'+ then eval env y+ else pure $ ValuePrim $ BoolValue False+ | baseString (qualLeaf op) == "||" = do+ x' <- asBool <$> eval env x+ if x'+ then pure $ ValuePrim $ BoolValue True+ else eval env y+ | otherwise = do+ x' <- evalArg env x xext+ y' <- evalArg env y yext+ op' <- eval env $ Var op op_t loc+ apply2 loc env op' x' y'+evalAppExp env (If cond e1 e2 _) = do cond' <- asBool <$> eval env cond if cond' then eval env e1 else eval env e2-evalAppExp env _ (Apply f args loc) = do+evalAppExp env (Apply f args loc) = do -- It is important that 'arguments' are evaluated in reverse order -- in order to bring any sizes into scope that may be used in the -- type of the functions.@@ -791,11 +837,11 @@ foldM (apply loc env) f' args' where evalArg' (Info (_, ext), x) = evalArg env x ext-evalAppExp env _ (Index e is loc) = do+evalAppExp env (Index e is loc) = do is' <- mapM (evalDimIndex env) is arr <- eval env e evalIndex loc env is' arr-evalAppExp env _ (LetWith dest src is v body loc) = do+evalAppExp env (LetWith dest src is v body loc) = do let Ident src_vn (Info src_t) _ = src dest' <- maybe oob pure@@ -807,7 +853,7 @@ eval (valEnv (M.singleton (identName dest) (Just t, dest')) <> env) body where oob = bad loc env "Update out of bounds"-evalAppExp env _ (DoLoop sparams pat init_e form body _) = do+evalAppExp env (DoLoop sparams pat init_e form body _) = do init_v <- eval env init_e case form of For iv bound -> do@@ -858,7 +904,7 @@ env' <- withLoopParams v env'' <- matchPat env' in_pat in_v evalBody env''-evalAppExp env _ (Match e cs _) = do+evalAppExp env (Match e cs _) = do v <- eval env e match v (NE.toList cs) where@@ -902,10 +948,28 @@ vs' <- mapM (eval env) vs pure $ toArray' (valueShape v') (v' : vs') eval env (AppExp e (Info (AppRes t retext))) = do- t' <- evalType env $ toStruct t- returned env t' retext =<< evalAppExp env t' e+ let t' = expandType env $ toStruct t+ v <- evalAppExp env e+ returned env t' retext v eval env (Var qv (Info t) _) = evalTermVar env qv (toStruct t) eval env (Ascript e _ _) = eval env e+eval env (Coerce e te (Info t) loc) = do+ v <- eval env e+ eval' <- evalWithExts env+ t' <- evalType eval' mempty $ expandType env $ toStruct t+ case checkShape (structTypeShape t') (valueShape v) of+ Just _ -> pure v+ Nothing ->+ bad loc env . docText $+ "Value `"+ <> prettyValue v+ <> "` of shape `"+ <> pretty (valueShape v)+ <> "` cannot match shape of type `"+ <> pretty te+ <> "` (`"+ <> pretty t'+ <> "`)" eval _ (IntLit v (Info t) _) = case t of Scalar (Prim (Signed it)) ->@@ -960,7 +1024,7 @@ -- that takes an empty tuple '()' as argument! Zero-parameter lambdas -- can never occur in a well-formed Futhark program, but they are -- convenient in the interpreter.-eval env (Lambda ps body _ (Info (_, RetType _ rt)) _) =+eval env (Lambda ps body _ (Info (RetType _ rt)) _) = evalFunction env [] ps body rt eval env (OpSection qv (Info t) _) = evalTermVar env qv $ toStruct t@@ -1044,9 +1108,9 @@ onTerm (TermPoly t v) = TermPoly t v onTerm (TermModule m) = TermModule $ onModule m onType (T.TypeAbbr l ps t) = T.TypeAbbr l ps $ first onDim t- onDim (NamedSize v) = NamedSize $ replaceQ v- onDim (ConstSize x) = ConstSize x- onDim (AnySize v) = AnySize v+ onDim (Var v typ loc) = Var (replaceQ v) typ loc+ onDim (IntLit x t loc) = IntLit x t loc+ onDim _ = error "Arbitrary expression not supported yet" evalModuleVar :: Env -> QualName VName -> EvalM Module evalModuleVar env qv =@@ -1095,9 +1159,11 @@ _ -> error "Expected ModuleFun." evalDec :: Env -> Dec -> EvalM Env-evalDec env (ValDec (ValBind _ v _ (Info ret) tparams ps fbody _ _ _)) = do+evalDec env (ValDec (ValBind _ v _ (Info ret) tparams ps fbody _ _ _)) = localExts $ do binding <- evalFunctionBinding env tparams ps ret fbody- pure $ env {envTerm = M.insert v binding $ envTerm env}+ sizes <- extSizeEnv+ pure $+ env {envTerm = M.insert v binding $ envTerm env} <> sizes evalDec env (OpenDec me _) = do me' <- evalModExp env me case me' of@@ -1472,9 +1538,10 @@ def s | "reduce_stream" `isPrefixOf` s = Just $ fun3 $ \_ f arg -> stream f arg def "map" = Just $- TermPoly Nothing $ \t -> pure $- ValueFun $ \f -> pure . ValueFun $ \xs ->- case unfoldFunType t of+ TermPoly Nothing $ \t eval' -> do+ t' <- evalType eval' mempty t+ pure $ ValueFun $ \f -> pure . ValueFun $ \xs ->+ case unfoldFunType t' of ([_, _], ret_t) | Just rowshape <- typeRowShape ret_t -> toArray' rowshape <$> mapM (apply noLoc mempty f) (snd $ fromArray xs)@@ -1600,10 +1667,10 @@ ( ValueArray dest_shape dest_arr, ValueArray _ vs_arr ) -> do- let acc = ValueAcc (\_ x -> pure x) dest_arr+ let acc = ValueAcc dest_shape (\_ x -> pure x) dest_arr acc' <- foldM (apply2 noLoc mempty f) acc vs_arr case acc' of- ValueAcc _ dest_arr' ->+ ValueAcc _ _ dest_arr' -> pure $ ValueArray dest_shape dest_arr' _ -> error $ "scatter_stream produced: " <> show acc'@@ -1615,10 +1682,10 @@ ( ValueArray dest_shape dest_arr, ValueArray _ vs_arr ) -> do- let acc = ValueAcc (apply2 noLoc mempty op) dest_arr+ let acc = ValueAcc dest_shape (apply2 noLoc mempty op) dest_arr acc' <- foldM (apply2 noLoc mempty f) acc vs_arr case acc' of- ValueAcc _ dest_arr' ->+ ValueAcc _ _ dest_arr' -> pure $ ValueArray dest_shape dest_arr' _ -> error $ "hist_stream produced: " <> show acc'@@ -1627,14 +1694,14 @@ def "acc_write" = Just $ fun3 $ \acc i v -> case (acc, i) of- ( ValueAcc op acc_arr,+ ( ValueAcc shape op acc_arr, ValuePrim (SignedValue (Int64Value i')) ) -> if i' >= 0 && i' < arrayLength acc_arr then do let x = acc_arr ! fromIntegral i' res <- op x v- pure $ ValueAcc op $ acc_arr // [(fromIntegral i', res)]+ pure $ ValueAcc shape op $ acc_arr // [(fromIntegral i', res)] else pure acc _ -> error $ "acc_write invalid arguments: " <> prettyString (show acc, show i, show v)@@ -1792,18 +1859,6 @@ -- Slight hack to work around empty dimensions. genericTake m $ transpose (map (snd . fromArray) xs') ++ repeat []- def "rotate" = Just $- fun2 $ \i xs -> do- let (shape, xs') = fromArray xs- pure $- let idx = if null xs' then 0 else rem (asInt i) (length xs')- in if idx > 0- then- let (bef, aft) = splitAt idx xs'- in toArray shape $ aft ++ bef- else- let (bef, aft) = splitFromEnd (-idx) xs'- in toArray shape $ aft ++ bef def "flatten" = Just $ fun1 $ \xs -> do let (ShapeDim n (ShapeDim m shape), xs') = fromArray xs@@ -1813,7 +1868,7 @@ let (ShapeDim xs_size innershape, xs') = fromArray xs rowshape = ShapeDim (asInt64 m) innershape shape = ShapeDim (asInt64 n) rowshape- if asInt64 n * asInt64 m /= xs_size+ if asInt64 n * asInt64 m /= xs_size || asInt64 n < 0 || asInt64 m < 0 then bad mempty mempty $ "Cannot unflatten array of shape ["@@ -1846,20 +1901,24 @@ interpretExp :: Ctx -> Exp -> F ExtOp Value interpretExp ctx e = runEvalM (ctxImports ctx) $ eval (ctxEnv ctx) e -interpretDec :: Ctx -> Dec -> F ExtOp Ctx-interpretDec ctx d = do- env <- runEvalM (ctxImports ctx) $ do- env <- evalDec (ctxEnv ctx) d+interpretDecs :: Ctx -> [Dec] -> F ExtOp Env+interpretDecs ctx decs =+ runEvalM (ctxImports ctx) $ do+ env <- foldM evalDec (ctxEnv ctx) decs -- We need to extract any new existential sizes and add them as -- ordinary bindings to the context, or we will not be able to -- look up their values later. sizes <- extSizeEnv pure $ env <> sizes++interpretDec :: Ctx -> Dec -> F ExtOp Ctx+interpretDec ctx d = do+ env <- interpretDecs ctx [d] pure ctx {ctxEnv = env} interpretImport :: Ctx -> (ImportName, Prog) -> F ExtOp Ctx interpretImport ctx (fp, prog) = do- env <- runEvalM (ctxImports ctx) $ foldM evalDec (ctxEnv ctx) $ progDecs prog+ env <- interpretDecs ctx $ progDecs prog pure ctx {ctxImports = M.insert fp env $ ctxImports ctx} -- | Produce a context, based on the one passed in, where all of@@ -1870,7 +1929,7 @@ valueType :: V.Value -> ValueType valueType v = let V.ValueType shape pt = V.valueType v- in arrayOf mempty (F.Shape (map fromIntegral shape)) (Scalar (Prim (toPrim pt)))+ in arrayOf (F.Shape (map fromIntegral shape)) (Scalar (Prim (toPrim pt))) where toPrim V.I8 = Signed Int8 toPrim V.I16 = Signed Int16@@ -1887,7 +1946,7 @@ checkEntryArgs :: VName -> [V.Value] -> StructType -> Either T.Text () checkEntryArgs entry args entry_t- | args_ts == map snd param_ts =+ | args_ts == map toStruct param_ts = pure () | otherwise = Left . docText $@@ -1922,12 +1981,16 @@ Right $ runEvalM (ctxImports ctx) $ do- f <- evalTermVar (ctxEnv ctx) (qualName fname) ft+ -- XXX: We are providing a dummy type here. This is OK as long+ -- as the function we invoke is monomorphic, which is what we+ -- require of entry points. This is to avoid reimplementing+ -- type inference machinery here.+ f <- evalTermVar (ctxEnv ctx) (qualName fname) (Scalar (Prim Bool)) foldM (apply noLoc mempty) f vs' where updateType (vt : vts) (Scalar (Arrow als pn d pt (RetType dims rt))) = do checkInput vt pt- Scalar . Arrow als pn d (valueStructType vt) . RetType dims <$> updateType vts rt+ Scalar . Arrow als pn d (valueStructType vt) . RetType dims . toRes Nonunique <$> updateType vts (toStruct rt) updateType _ t = Right t @@ -1935,7 +1998,7 @@ checkInput :: ValueType -> StructType -> Either T.Text () checkInput (Scalar (Prim vt)) (Scalar (Prim pt)) | vt /= pt = badPrim vt pt- checkInput (Array _ _ _ (Prim vt)) (Array _ _ _ (Prim pt))+ checkInput (Array _ _ (Prim vt)) (Array _ _ (Prim pt)) | vt /= pt = badPrim vt pt checkInput _ _ = Right ()
src/Language/Futhark/Interpreter/Values.hs view
@@ -76,20 +76,23 @@ emptyShape (ShapeDim d s) = d == 0 || emptyShape s emptyShape _ = False -typeShape :: TypeBase d () -> Shape d-typeShape (Array _ _ shape et) =+typeShape :: TypeBase d u -> Shape d+typeShape (Array _ shape et) = foldr ShapeDim (typeShape (Scalar et)) $ shapeDims shape typeShape (Scalar (Record fs)) = ShapeRecord $ M.map typeShape fs typeShape (Scalar (Sum cs)) = ShapeSum $ M.map (map typeShape) cs-typeShape _ =- ShapeLeaf+typeShape t+ | Just t' <- isAccType t =+ typeShape t'+ | otherwise =+ ShapeLeaf structTypeShape :: StructType -> Shape (Maybe Int64) structTypeShape = fmap dim . typeShape where- dim (ConstSize d) = Just $ fromIntegral d+ dim (IntLit x _ _) = Just $ fromIntegral x dim _ = Nothing -- | A fully evaluated Futhark value.@@ -101,8 +104,8 @@ | ValueFun (Value m -> m (Value m)) | -- Stores the full shape. ValueSum ValueShape Name [Value m]- | -- The update function and the array.- ValueAcc (Value m -> Value m -> m (Value m)) !(Array Int (Value m))+ | -- The shape, the update function, and the array.+ ValueAcc ValueShape (Value m -> Value m -> m (Value m)) !(Array Int (Value m)) instance Show (Value m) where show (ValuePrim v) = "ValuePrim " <> show v <> ""@@ -124,7 +127,7 @@ ValueArray _ x == ValueArray _ y = x == y ValueRecord x == ValueRecord y = x == y ValueSum _ n1 vs1 == ValueSum _ n2 vs2 = n1 == n2 && vs1 == vs2- ValueAcc _ x == ValueAcc _ y = x == y+ ValueAcc _ _ x == ValueAcc _ _ y = x == y _ == _ = False prettyValueWith :: (PrimValue -> Doc a) -> Value m -> Doc a@@ -174,6 +177,7 @@ valueShape :: Value m -> ValueShape valueShape (ValueArray shape _) = shape+valueShape (ValueAcc shape _ _) = shape valueShape (ValueRecord fs) = ShapeRecord $ M.map valueShape fs valueShape (ValueSum shape _ _) = shape valueShape _ = ShapeLeaf
src/Language/Futhark/Parser.hs view
@@ -1,11 +1,13 @@ -- | Interface to the Futhark parser. module Language.Futhark.Parser ( parseFuthark,+ parseFutharkWithComments, parseExp, parseModExp, parseType, parseDecOrExpIncrM, SyntaxError (..),+ Comment (..), ) where @@ -22,6 +24,15 @@ Either SyntaxError UncheckedProg parseFuthark = parse prog +-- | Parse an entire Futhark program from the given 'T.Text', using+-- the 'FilePath' as the source name for error messages. Also returns+-- the comments encountered.+parseFutharkWithComments ::+ FilePath ->+ T.Text ->+ Either SyntaxError (UncheckedProg, [Comment])+parseFutharkWithComments = parseWithComments prog+ -- | Parse an Futhark expression from the given 'String', using the -- 'FilePath' as the source name for error messages. parseExp ::@@ -55,7 +66,7 @@ T.Text -> m (Either SyntaxError UncheckedExp) parseExpIncrM fetch file program =- getLinesFromM fetch $ parseInMonad expression file program+ getLinesFromM fetch $ fmap fst <$> parseInMonad expression file program -- | Parse either an expression or a declaration incrementally; -- favouring declarations in case of ambiguity.@@ -68,7 +79,7 @@ parseDecOrExpIncrM fetch file input = case parseInMonad declaration file input of Value Left {} -> fmap Right <$> parseExpIncrM fetch file input- Value (Right d) -> pure $ Right $ Left d+ Value (Right (d, _)) -> pure $ Right $ Left d GetLine _ -> do l <- fetch parseDecOrExpIncrM fetch file $ input <> "\n" <> l
src/Language/Futhark/Parser/Lexer.x view
@@ -54,7 +54,7 @@ tokens :- $white+ ;- @doc { tokenM $ pure . DOC . T.unpack . T.unlines .+ @doc { tokenM $ pure . DOC . T.intercalate "\n" . map (T.drop 3 . T.stripStart) . T.split (== '\n') . ("--"<>) . T.drop 4 }
src/Language/Futhark/Parser/Lexer/Tokens.hs view
@@ -128,7 +128,7 @@ | LOCAL | MATCH | CASE- | DOC String+ | DOC T.Text | EOF | HOLE deriving (Show, Eq, Ord)
src/Language/Futhark/Parser/Monad.hs view
@@ -7,8 +7,10 @@ ( ParserMonad, ParserState, ReadLineMonad (..),+ Comment (..), parseInMonad, parse,+ parseWithComments, getLinesFromM, lexer, mustBeEmpty,@@ -85,14 +87,24 @@ "Only the keyword '" <> expected <> "' may appear here." mustBeEmpty :: Located loc => loc -> ValueType -> ParserMonad ()-mustBeEmpty _ (Array _ _ (Shape dims) _)+mustBeEmpty _ (Array _ (Shape dims) _) | 0 `elem` dims = pure () mustBeEmpty loc t = parseErrorAt loc $ Just $ prettyText t <> " is not an empty array." +-- | A comment consists of its starting and end position, as well as+-- its text. The contents include the comment start marker.+data Comment = Comment {commentLoc :: Loc, commentText :: T.Text}+ deriving (Eq, Ord, Show)++instance Located Comment where+ locOf = commentLoc+ data ParserState = ParserState { _parserFile :: FilePath, parserInput :: T.Text,+ -- | Note: reverse order.+ parserComments :: [Comment], parserLexical :: ([L Token], Pos) } @@ -144,7 +156,7 @@ index = AppExp (Index e (is ++ map DimFix xs) xloc) NoInfo op f x = pure $ mkApplyUT f x -patternExp :: UncheckedPat -> ParserMonad UncheckedExp+patternExp :: UncheckedPat t -> ParserMonad UncheckedExp patternExp (Id v _ loc) = pure $ Var (qualName v) NoInfo loc patternExp (TuplePat pats loc) = TupLit <$> mapM patternExp pats <*> pure loc patternExp (Wildcard _ loc) = parseErrorAt loc $ Just "cannot have wildcard here."@@ -175,6 +187,10 @@ putTokens :: ([L Token], Pos) -> ParserMonad () putTokens l = lift $ modify $ \env -> env {parserLexical = l} +putComment :: Comment -> ParserMonad ()+putComment c = lift $ modify $ \env ->+ env {parserComments = c : parserComments env}+ intNegate :: IntValue -> IntValue intNegate (Int8Value v) = Int8Value (-v) intNegate (Int16Value v) = Int16Value (-v)@@ -221,7 +237,8 @@ xs -> do putTokens (xs, pos') lexer cont- (L _ (COMMENT _) : xs) -> do+ (L loc (COMMENT text) : xs) -> do+ putComment $ Comment loc text putTokens (xs, pos) lexer cont (x : xs) -> do@@ -271,14 +288,23 @@ lexerErrToParseErr :: LexerError -> SyntaxError lexerErrToParseErr (LexerError loc msg) = SyntaxError loc msg -parseInMonad :: ParserMonad a -> FilePath -> T.Text -> ReadLineMonad (Either SyntaxError a)+parseInMonad ::+ ParserMonad a ->+ FilePath ->+ T.Text ->+ ReadLineMonad (Either SyntaxError (a, [Comment])) parseInMonad p file program = either (pure . Left . lexerErrToParseErr)- (evalStateT (runExceptT p) . env)+ (fmap onRes . runStateT (runExceptT p) . env) (scanTokensText (Pos file 1 1 0) program) where- env = ParserState file program+ env = ParserState file program []+ onRes (Left err, _) = Left err+ onRes (Right x, s) = Right (x, reverse $ parserComments s) +parseWithComments :: ParserMonad a -> FilePath -> T.Text -> Either SyntaxError (a, [Comment])+parseWithComments p file program = join $ getNoLines $ parseInMonad p file program+ parse :: ParserMonad a -> FilePath -> T.Text -> Either SyntaxError a-parse p file program = join $ getNoLines $ parseInMonad p file program+parse p file program = fst <$> parseWithComments p file program
src/Language/Futhark/Parser/Parser.y view
@@ -7,10 +7,12 @@ , modExpression , futharkType , parse+ , parseWithComments , ReadLineMonad (..) , getLinesFromM , parseInMonad , SyntaxError(..)+ , Comment(..) ) where @@ -523,14 +525,14 @@ | '...[' Exp ']' { SizeExp $2 (srcspan $1 $>) } | '...[' ']' { SizeExpAny (srcspan $1 $>) } -FunParam :: { PatBase NoInfo Name }-FunParam : InnerPat { $1 }+FunParam :: { PatBase NoInfo Name ParamType }+FunParam : InnerPat { fmap (toParam Observe) $1 } -FunParams1 :: { (PatBase NoInfo Name, [PatBase NoInfo Name]) }+FunParams1 :: { (PatBase NoInfo Name ParamType, [PatBase NoInfo Name ParamType]) } FunParams1 : FunParam { ($1, []) } | FunParam FunParams1 { ($1, fst $2 : snd $2) } -FunParams :: { [PatBase NoInfo Name] }+FunParams :: { [PatBase NoInfo Name ParamType ] } FunParams : { [] } | FunParam FunParams { $1 : $2 } @@ -547,7 +549,7 @@ -- array slices). Exp :: { UncheckedExp } : Exp ':' TypeExp { Ascript $1 $3 (srcspan $1 $>) }- | Exp ':>' TypeExp { AppExp (Coerce $1 $3 (srcspan $1 $>)) NoInfo }+ | Exp ':>' TypeExp { Coerce $1 $3 NoInfo (srcspan $1 $>) } | Exp2 %prec ':' { $1 } Exp2 :: { UncheckedExp }@@ -766,9 +768,9 @@ LoopExp :: { UncheckedExp } : loop Pat LoopForm do Exp %prec ifprec- {% fmap (\t -> AppExp (DoLoop [] $2 t $3 $5 (srcspan $1 $>)) NoInfo) (patternExp $2) }+ {% fmap (\t -> AppExp (DoLoop [] (fmap (toParam Observe) $2) t $3 $5 (srcspan $1 $>)) NoInfo) (patternExp $2) } | loop Pat '=' Exp LoopForm do Exp %prec ifprec- { AppExp (DoLoop [] $2 $4 $5 $7 (srcspan $1 $>)) NoInfo }+ { AppExp (DoLoop [] (fmap (toParam Observe) $2) $4 $5 $7 (srcspan $1 $>)) NoInfo } MatchExp :: { UncheckedExp } : match Exp Cases@@ -783,7 +785,7 @@ : case CPat '->' Exp { let loc = srcspan $1 $> in CasePat $2 $> loc } -CPat :: { PatBase NoInfo Name }+CPat :: { PatBase NoInfo Name StructType } : '#[' AttrInfo ']' CPat { PatAttr $2 $4 (srcspan $1 $>) } | CInnerPat ':' TypeExp { PatAscription $1 $3 (srcspan $1 $>) } | CInnerPat { $1 }@@ -791,11 +793,11 @@ loc' = srcspan loc $> in PatConstr n NoInfo $2 loc'} -CPats1 :: { [PatBase NoInfo Name] }+CPats1 :: { [PatBase NoInfo Name StructType] } : CPat { [$1] } | CPat ',' CPats1 { $1 : $3 } -CInnerPat :: { PatBase NoInfo Name }+CInnerPat :: { PatBase NoInfo Name StructType } : id { let L loc (ID name) = $1 in Id name NoInfo (srclocOf loc) } | '(' BindingBinOp ')' { Id $2 NoInfo (srcspan $1 $>) } | '_' { Wildcard NoInfo (srclocOf $1) }@@ -807,11 +809,11 @@ | Constr { let (n, loc) = $1 in PatConstr n NoInfo [] (srclocOf loc) } -ConstrFields :: { [PatBase NoInfo Name] }+ConstrFields :: { [PatBase NoInfo Name StructType] } : CInnerPat { [$1] } | ConstrFields CInnerPat { $1 ++ [$2] } -CFieldPat :: { (Name, PatBase NoInfo Name) }+CFieldPat :: { (Name, PatBase NoInfo Name StructType) } : FieldId '=' CPat { (fst $1, $3) } | FieldId ':' TypeExp@@ -819,11 +821,11 @@ | FieldId { (fst $1, Id (fst $1) NoInfo (srclocOf (snd $1))) } -CFieldPats :: { [(Name, PatBase NoInfo Name)] }+CFieldPats :: { [(Name, PatBase NoInfo Name StructType)] } : CFieldPats1 { $1 } | { [] } -CFieldPats1 :: { [(Name, PatBase NoInfo Name)] }+CFieldPats1 :: { [(Name, PatBase NoInfo Name StructType)] } : CFieldPat ',' CFieldPats1 { $1 : $3 } | CFieldPat { [$1] } @@ -866,23 +868,23 @@ : DimIndex { ($1, []) } | DimIndex ',' DimIndices1 { ($1, fst $3 : snd $3) } -VarId :: { IdentBase NoInfo Name }+VarId :: { IdentBase NoInfo Name StructType } VarId : id { let L loc (ID name) = $1 in Ident name NoInfo (srclocOf loc) } FieldId :: { (Name, Loc) } : id { let L loc (ID name) = $1 in (name, loc) } | natlit { let L loc (NATLIT x _) = $1 in (x, loc) } -Pat :: { PatBase NoInfo Name }+Pat :: { PatBase NoInfo Name StructType } : '#[' AttrInfo ']' Pat { PatAttr $2 $4 (srcspan $1 $>) } | InnerPat ':' TypeExp { PatAscription $1 $3 (srcspan $1 $>) } | InnerPat { $1 } -Pats1 :: { [PatBase NoInfo Name] }+Pats1 :: { [PatBase NoInfo Name StructType] } : Pat { [$1] } | Pat ',' Pats1 { $1 : $3 } -InnerPat :: { PatBase NoInfo Name }+InnerPat :: { PatBase NoInfo Name StructType } InnerPat : id { let L loc (ID name) = $1 in Id name NoInfo (srclocOf loc) } | '(' BindingBinOp ')' { Id $2 NoInfo (srcspan $1 $>) } | '_' { Wildcard NoInfo (srclocOf $1) }@@ -891,7 +893,7 @@ | '(' Pat ',' Pats1 ')' { TuplePat ($2:$4) (srcspan $1 $>) } | '{' FieldPats '}' { RecordPat $2 (srcspan $1 $>) } -FieldPat :: { (Name, PatBase NoInfo Name) }+FieldPat :: { (Name, PatBase NoInfo Name StructType) } : FieldId '=' Pat { (fst $1, $3) } | FieldId ':' TypeExp@@ -899,11 +901,11 @@ | FieldId { (fst $1, Id (fst $1) NoInfo (srclocOf (snd $1))) } -FieldPats :: { [(Name, PatBase NoInfo Name)] }+FieldPats :: { [(Name, PatBase NoInfo Name StructType)] } : FieldPats1 { $1 } | { [] } -FieldPats1 :: { [(Name, PatBase NoInfo Name)] }+FieldPats1 :: { [(Name, PatBase NoInfo Name StructType)] } : FieldPat ',' FieldPats1 { $1 : $3 } | FieldPat { [$1] }
src/Language/Futhark/Pretty.hs view
@@ -20,6 +20,7 @@ import Data.Maybe import Data.Monoid hiding (Sum) import Data.Ord+import Data.Text qualified as T import Data.Word import Futhark.Util import Futhark.Util.Pretty@@ -80,12 +81,6 @@ pretty (BoolValue False) = "false" pretty (FloatValue v) = pretty v -instance Pretty Size where- pretty (AnySize Nothing) = mempty- pretty (AnySize (Just v)) = "?" <> prettyName v- pretty (NamedSize v) = pretty v- pretty (ConstSize n) = pretty n- instance (Eq vn, IsName vn, Annot f) => Pretty (SizeExp f vn) where pretty SizeExpAny {} = brackets mempty pretty (SizeExp e _) = brackets $ pretty e@@ -102,7 +97,7 @@ instance Pretty (Shape Bool) where pretty (Shape ds) = mconcat (map (brackets . pretty) ds) -prettyRetType :: Pretty (Shape dim) => Int -> RetTypeBase dim as -> Doc a+prettyRetType :: (Pretty (Shape dim), Pretty u) => Int -> RetTypeBase dim u -> Doc a prettyRetType p (RetType [] t) = prettyType p t prettyRetType _ (RetType dims t) =@@ -111,16 +106,16 @@ <> "." <> pretty t -instance Pretty (Shape dim) => Pretty (RetTypeBase dim as) where+instance (Pretty (Shape dim), Pretty u) => Pretty (RetTypeBase dim u) where pretty = prettyRetType 0 instance Pretty Diet where pretty Consume = "*" pretty Observe = "" -prettyScalarType :: Pretty (Shape dim) => Int -> ScalarTypeBase dim as -> Doc a+prettyScalarType :: (Pretty (Shape dim), Pretty u) => Int -> ScalarTypeBase dim u -> Doc a prettyScalarType _ (Prim et) = pretty et-prettyScalarType p (TypeVar _ u v targs) =+prettyScalarType p (TypeVar u v targs) = parensIf (not (null targs) && p > 3) $ pretty u <> hsep (pretty v : map (prettyTypeArg 3) targs) prettyScalarType _ (Record fs)@@ -148,21 +143,21 @@ ppConstr (name, fs) = sep $ ("#" <> pretty name) : map (prettyType 2) fs cs' = map ppConstr $ M.toList cs -instance Pretty (Shape dim) => Pretty (ScalarTypeBase dim as) where+instance (Pretty (Shape dim), Pretty u) => Pretty (ScalarTypeBase dim u) where pretty = prettyScalarType 0 -prettyType :: Pretty (Shape dim) => Int -> TypeBase dim as -> Doc a-prettyType _ (Array _ u shape at) =+prettyType :: (Pretty (Shape dim), Pretty u) => Int -> TypeBase dim u -> Doc a+prettyType _ (Array u shape at) = pretty u <> pretty shape <> align (prettyScalarType 1 at) prettyType p (Scalar t) = prettyScalarType p t -instance Pretty (Shape dim) => Pretty (TypeBase dim as) where+instance (Pretty (Shape dim), Pretty u) => Pretty (TypeBase dim u) where pretty = prettyType 0 prettyTypeArg :: Pretty (Shape dim) => Int -> TypeArg dim -> Doc a-prettyTypeArg _ (TypeArgDim d _) = pretty $ Shape [d]-prettyTypeArg p (TypeArgType t _) = prettyType p t+prettyTypeArg _ (TypeArgDim d) = pretty $ Shape [d]+prettyTypeArg p (TypeArgType t) = prettyType p t instance Pretty (TypeArg Size) where pretty = prettyTypeArg 0@@ -196,7 +191,7 @@ pretty (QualName names name) = mconcat $ punctuate "." $ map prettyName names ++ [prettyName name] -instance IsName vn => Pretty (IdentBase f vn) where+instance IsName vn => Pretty (IdentBase f vn t) where pretty = prettyName . identName hasArrayLit :: ExpBase ty vn -> Bool@@ -229,8 +224,6 @@ letBody body = "in" <+> align (pretty body) prettyAppExp :: (Eq vn, IsName vn, Annot f) => Int -> AppExpBase f vn -> Doc a-prettyAppExp p (Coerce e t _) =- parensIf (p /= -1) $ prettyExp 0 e <+> ":>" <+> align (pretty t) prettyAppExp p (BinOp (bop, _) _ (x, _) (y, _) _) = prettyBinOp p bop x y prettyAppExp _ (Match e cs _) = "match" <+> pretty e </> (stack . map pretty) (NE.toList cs) prettyAppExp _ (DoLoop sizeparams pat initexp form loopbody _) =@@ -256,8 +249,10 @@ where linebreak = case e of AppExp {} -> True+ Coerce {} -> True Attr {} -> True ArrayLit {} -> False+ Lambda {} -> True _ -> hasArrayLit e prettyAppExp _ (LetFun fname (tparams, params, retdecl, rettype, e) body _) = "let"@@ -310,7 +305,7 @@ instance (Eq vn, IsName vn, Annot f) => Pretty (AppExpBase f vn) where pretty = prettyAppExp (-1) -prettyInst :: Annot f => f PatType -> Doc a+prettyInst :: (Annot f, Pretty t) => f t -> Doc a prettyInst t = case unAnnot t of Just t'@@ -321,13 +316,25 @@ prettyAttr :: Pretty a => a -> Doc ann prettyAttr attr = "#[" <> pretty attr <> "]" +operatorName :: Name -> Bool+operatorName = (`elem` opchars) . T.head . nameToText+ where+ opchars :: String+ opchars = "+-*/%=!><|&^."+ prettyExp :: (Eq vn, IsName vn, Annot f) => Int -> ExpBase f vn -> Doc a-prettyExp _ (Var name t _) = pretty name <> prettyInst t+prettyExp _ (Var name t _)+ -- The first case occurs only for programs that have been normalised+ -- by the compiler.+ | operatorName (toName (qualLeaf name)) = parens $ pretty name <> prettyInst t+ | otherwise = pretty name <> prettyInst t prettyExp _ (Hole t _) = "???" <> prettyInst t prettyExp _ (Parens e _) = align $ parens $ pretty e prettyExp _ (QualParens (v, _) e _) = pretty v <> "." <> align (parens $ pretty e) prettyExp p (Ascript e t _) = parensIf (p /= -1) $ prettyExp 0 e <+> ":" <+> align (pretty t)+prettyExp p (Coerce e t _ _) =+ parensIf (p /= -1) $ prettyExp 0 e <+> ":>" <+> align (pretty t) prettyExp _ (Literal v _) = pretty v prettyExp _ (IntLit v t _) = pretty v <> prettyInst t prettyExp _ (FloatLit v t _) = pretty v <> prettyInst t@@ -359,7 +366,8 @@ <+> mconcat (intersperse "." (map pretty fs)) <+> "=" <+> align (pretty ve)-prettyExp _ (Assert e1 e2 _ _) = "assert" <+> prettyExp 10 e1 <+> prettyExp 10 e2+prettyExp _ (Assert e1 e2 _ _) =+ "assert" <+> prettyExp 10 e1 <+> prettyExp 10 e2 prettyExp p (Lambda params body rettype _ _) = parensIf (p /= -1) $ "\\" <> hsep (map pretty params) <> ppAscription rettype@@ -384,11 +392,11 @@ prettyAttr attr </> prettyExp (-1) e prettyExp i (AppExp e res) | isEnvVarAtLeast "FUTHARK_COMPILER_DEBUGGING" 2,- Just res' <- unAnnot res,- not $ null $ appResExt res' =- "#"- <> brackets (commasep $ map prettyName $ appResExt res')- </> parens (prettyAppExp i e)+ Just (AppRes t ext) <- unAnnot res,+ not $ null ext =+ parens (prettyAppExp i e)+ </> "@"+ <> parens (pretty t <> "," <+> brackets (commasep $ map prettyName ext)) | otherwise = prettyAppExp i e instance (Eq vn, IsName vn, Annot f) => Pretty (ExpBase f vn) where@@ -422,7 +430,7 @@ pretty (PatLitFloat f) = pretty f pretty (PatLitPrim v) = pretty v -instance (Eq vn, IsName vn, Annot f) => Pretty (PatBase f vn) where+instance (Eq vn, IsName vn, Annot f, Pretty t) => Pretty (PatBase f vn t) where pretty (PatAscription p t _) = pretty p <> colon <+> align (pretty t) pretty (PatParens p _) = parens $ pretty p pretty (Id v t _) = case unAnnot t of
src/Language/Futhark/Primitive.hs view
@@ -1685,6 +1685,9 @@ primBitSize = (* 8) . primByteSize -- | The size of a value of a given primitive type in eight-bit bytes.+--+-- Warning: note that this is 0 for 'Unit', but a 'Unit' takes up a+-- byte in the binary data format. primByteSize :: Num a => PrimType -> a primByteSize (IntType t) = intByteSize t primByteSize (FloatType t) = floatByteSize t
src/Language/Futhark/Prop.hs view
@@ -6,1403 +6,1522 @@ ( -- * Various Intrinsic (..), intrinsics,- isBuiltin,- isBuiltinLoc,- maxIntrinsicTag,- namesToPrimTypes,- qualName,- qualify,- primValueType,- leadingOperator,- progImports,- decImports,- progModuleTypes,- identifierReference,- prettyStacktrace,- progHoles,- defaultEntryPoint,- paramName,-- -- * Queries on expressions- typeOf,- valBindTypeScheme,- valBindBound,- funType,-- -- * Queries on patterns and params- patIdents,- patNames,- patternMap,- patternType,- patternStructType,- patternParam,- patternOrderZero,-- -- * Queries on types- uniqueness,- unique,- aliases,- diet,- arrayRank,- arrayShape,- orderZero,- unfoldFunType,- foldFunType,- foldFunTypeFromParams,- typeVars,-- -- * Operations on types- peelArray,- stripArray,- arrayOf,- toStructural,- toStruct,- fromStruct,- setAliases,- addAliases,- setUniqueness,- noSizes,- traverseDims,- DimPos (..),- tupleRecord,- isTupleRecord,- areTupleFields,- tupleFields,- tupleFieldNames,- sortFields,- sortConstrs,- isTypeParam,- isSizeParam,- combineTypeShapes,- matchDims,- -- | Values of these types are produces by the parser. They use- -- unadorned names and have no type information, apart from that- -- which is syntactically required.- NoInfo (..),- UncheckedType,- UncheckedTypeExp,- UncheckedIdent,- UncheckedDimIndex,- UncheckedSlice,- UncheckedExp,- UncheckedModExp,- UncheckedSigExp,- UncheckedTypeParam,- UncheckedPat,- UncheckedValBind,- UncheckedDec,- UncheckedSpec,- UncheckedProg,- UncheckedCase,- )-where--import Control.Monad-import Control.Monad.State-import Data.Bifoldable-import Data.Bifunctor-import Data.Bitraversable (bitraverse)-import Data.Char-import Data.Foldable-import Data.List (genericLength, isPrefixOf, sortOn)-import Data.Loc (Loc (..), posFile)-import Data.Map.Strict qualified as M-import Data.Maybe-import Data.Ord-import Data.Set qualified as S-import Futhark.Util (maxinum)-import Futhark.Util.Pretty-import Language.Futhark.Primitive qualified as Primitive-import Language.Futhark.Syntax-import Language.Futhark.Traversals-import Language.Futhark.Tuple-import System.FilePath (takeDirectory)---- | The name of the default program entry point (@main@).-defaultEntryPoint :: Name-defaultEntryPoint = nameFromString "main"---- | Return the dimensionality of a type. For non-arrays, this is--- zero. For a one-dimensional array it is one, for a two-dimensional--- it is two, and so forth.-arrayRank :: TypeBase dim as -> Int-arrayRank = shapeRank . arrayShape---- | Return the shape of a type - for non-arrays, this is 'mempty'.-arrayShape :: TypeBase dim as -> Shape dim-arrayShape (Array _ _ ds _) = ds-arrayShape _ = mempty---- | Change the shape of a type to be just the rank.-noSizes :: TypeBase Size as -> TypeBase () as-noSizes = first $ const ()---- | Where does this dimension occur?-data DimPos- = -- | Immediately in the argument to 'traverseDims'.- PosImmediate- | -- | In a function parameter type.- PosParam- | -- | In a function return type.- PosReturn- deriving (Eq, Ord, Show)---- | Perform a traversal (possibly including replacement) on sizes--- that are parameters in a function type, but also including the type--- immediately passed to the function. Also passes along a set of the--- parameter names inside the type that have come in scope at the--- occurrence of the dimension.-traverseDims ::- forall f fdim tdim als.- Applicative f =>- (S.Set VName -> DimPos -> fdim -> f tdim) ->- TypeBase fdim als ->- f (TypeBase tdim als)-traverseDims f = go mempty PosImmediate- where- go ::- forall als'.- S.Set VName ->- DimPos ->- TypeBase fdim als' ->- f (TypeBase tdim als')- go bound b t@Array {} =- bitraverse (f bound b) pure t- go bound b (Scalar (Record fields)) =- Scalar . Record <$> traverse (go bound b) fields- go bound b (Scalar (TypeVar as u tn targs)) =- Scalar <$> (TypeVar as u tn <$> traverse (onTypeArg bound b) targs)- go bound b (Scalar (Sum cs)) =- Scalar . Sum <$> traverse (traverse (go bound b)) cs- go _ _ (Scalar (Prim t)) =- pure $ Scalar $ Prim t- go bound _ (Scalar (Arrow als p u t1 (RetType dims t2))) =- Scalar <$> (Arrow als p u <$> go bound' PosParam t1 <*> (RetType dims <$> go bound' PosReturn t2))- where- bound' =- S.fromList dims- <> case p of- Named p' -> S.insert p' bound- Unnamed -> bound-- onTypeArg bound b (TypeArgDim d loc) =- TypeArgDim <$> f bound b d <*> pure loc- onTypeArg bound b (TypeArgType t loc) =- TypeArgType <$> go bound b t <*> pure loc---- | Return the uniqueness of a type.-uniqueness :: TypeBase shape as -> Uniqueness-uniqueness (Array _ u _ _) = u-uniqueness (Scalar (TypeVar _ u _ _)) = u-uniqueness (Scalar (Sum ts))- | any (any unique) ts = Unique-uniqueness (Scalar (Record fs))- | any unique fs = Unique-uniqueness _ = Nonunique---- | @unique t@ is 'True' if the type of the argument is unique.-unique :: TypeBase shape as -> Bool-unique = (== Unique) . uniqueness---- | Return the set of all variables mentioned in the aliasing of a--- type.-aliases :: Monoid as => TypeBase shape as -> as-aliases = bifoldMap (const mempty) id---- | @diet t@ returns a description of how a function parameter of--- type @t@ consumes its argument.-diet :: TypeBase shape as -> Diet-diet (Scalar (Record ets)) = foldl max Observe $ fmap diet ets-diet (Scalar (Prim _)) = Observe-diet (Scalar (Arrow {})) = Observe-diet (Array _ Unique _ _) = Consume-diet (Array _ Nonunique _ _) = Observe-diet (Scalar (TypeVar _ Unique _ _)) = Consume-diet (Scalar (TypeVar _ Nonunique _ _)) = Observe-diet (Scalar (Sum cs)) = foldl max Observe $ foldMap (map diet) cs---- | Convert any type to one that has rank information, no alias--- information, and no embedded names.-toStructural ::- TypeBase dim as ->- TypeBase () ()-toStructural = flip setAliases () . first (const ())---- | Remove aliasing information from a type.-toStruct ::- TypeBase dim as ->- TypeBase dim ()-toStruct t = t `setAliases` ()---- | Replace no aliasing with an empty alias set.-fromStruct ::- TypeBase dim as ->- TypeBase dim Aliasing-fromStruct t = t `setAliases` S.empty---- | @peelArray n t@ returns the type resulting from peeling the first--- @n@ array dimensions from @t@. Returns @Nothing@ if @t@ has less--- than @n@ dimensions.-peelArray :: Int -> TypeBase dim as -> Maybe (TypeBase dim as)-peelArray n (Array als u shape t)- | shapeRank shape == n =- Just $ Scalar t `addAliases` const als- | otherwise =- Array als u <$> stripDims n shape <*> pure t-peelArray _ _ = Nothing---- | @arrayOf u s t@ constructs an array type. The convenience--- compared to using the 'Array' constructor directly is that @t@ can--- itself be an array. If @t@ is an @n@-dimensional array, and @s@ is--- a list of length @n@, the resulting type is of an @n+m@ dimensions.--- The uniqueness of the new array will be @u@, no matter the--- uniqueness of @t@.-arrayOf ::- Monoid as =>- Uniqueness ->- Shape dim ->- TypeBase dim as ->- TypeBase dim as-arrayOf = arrayOfWithAliases mempty--arrayOfWithAliases ::- Monoid as =>- as ->- Uniqueness ->- Shape dim ->- TypeBase dim as ->- TypeBase dim as-arrayOfWithAliases as2 u shape2 (Array as1 _ shape1 et) =- Array (as1 <> as2) u (shape2 <> shape1) et-arrayOfWithAliases as u shape (Scalar t) =- Array as u shape (second (const ()) t)---- | @stripArray n t@ removes the @n@ outermost layers of the array.--- Essentially, it is the type of indexing an array of type @t@ with--- @n@ indexes.-stripArray :: Int -> TypeBase dim as -> TypeBase dim as-stripArray n (Array als u shape et)- | Just shape' <- stripDims n shape =- Array als u shape' et- | otherwise =- Scalar et `setUniqueness` u `setAliases` als-stripArray _ t = t---- | Create a record type corresponding to a tuple with the given--- element types.-tupleRecord :: [TypeBase dim as] -> ScalarTypeBase dim as-tupleRecord = Record . M.fromList . zip tupleFieldNames---- | Does this type corespond to a tuple? If so, return the elements--- of that tuple.-isTupleRecord :: TypeBase dim as -> Maybe [TypeBase dim as]-isTupleRecord (Scalar (Record fs)) = areTupleFields fs-isTupleRecord _ = Nothing---- | Sort the constructors of a sum type in some well-defined (but not--- otherwise significant) manner.-sortConstrs :: M.Map Name a -> [(Name, a)]-sortConstrs cs = sortOn fst $ M.toList cs---- | Is this a 'TypeParamType'?-isTypeParam :: TypeParamBase vn -> Bool-isTypeParam TypeParamType {} = True-isTypeParam TypeParamDim {} = False---- | Is this a 'TypeParamDim'?-isSizeParam :: TypeParamBase vn -> Bool-isSizeParam = not . isTypeParam---- | Combine the shape information of types as much as possible. The first--- argument is the orignal type and the second is the type of the transformed--- expression. This is necessary since the original type may contain additional--- information (e.g., shape restrictions) from the user given annotation.-combineTypeShapes ::- (Monoid as) =>- TypeBase Size as ->- TypeBase Size as ->- TypeBase Size as-combineTypeShapes (Scalar (Record ts1)) (Scalar (Record ts2))- | M.keys ts1 == M.keys ts2 =- Scalar $- Record $- M.map- (uncurry combineTypeShapes)- (M.intersectionWith (,) ts1 ts2)-combineTypeShapes (Scalar (Sum cs1)) (Scalar (Sum cs2))- | M.keys cs1 == M.keys cs2 =- Scalar $- Sum $- M.map- (uncurry $ zipWith combineTypeShapes)- (M.intersectionWith (,) cs1 cs2)-combineTypeShapes (Scalar (Arrow als1 p1 d1 a1 (RetType dims1 b1))) (Scalar (Arrow als2 _p2 _d2 a2 (RetType _ b2))) =- Scalar $- Arrow- (als1 <> als2)- p1- d1- (combineTypeShapes a1 a2)- (RetType dims1 (combineTypeShapes b1 b2))-combineTypeShapes (Scalar (TypeVar als1 u1 v targs1)) (Scalar (TypeVar als2 _ _ targs2)) =- Scalar $ TypeVar (als1 <> als2) u1 v $ zipWith f targs1 targs2- where- f (TypeArgType t1 loc) (TypeArgType t2 _) =- TypeArgType (combineTypeShapes t1 t2) loc- f targ _ = targ-combineTypeShapes (Array als1 u1 shape1 et1) (Array als2 _u2 _shape2 et2) =- arrayOfWithAliases- (als1 <> als2)- u1- shape1- (combineTypeShapes (Scalar et1) (Scalar et2) `setAliases` mempty)-combineTypeShapes _ new_tp = new_tp---- | The name, if any.-paramName :: PName -> Maybe VName-paramName (Named v) = Just v-paramName Unnamed = Nothing---- | Match the dimensions of otherwise assumed-equal types. The--- combining function is also passed the names bound within the type--- (from named parameters or return types).-matchDims ::- forall as m d1 d2.- (Monoid as, Monad m) =>- ([VName] -> d1 -> d2 -> m d1) ->- TypeBase d1 as ->- TypeBase d2 as ->- m (TypeBase d1 as)-matchDims onDims = matchDims' mempty- where- matchDims' ::- forall as'. Monoid as' => [VName] -> TypeBase d1 as' -> TypeBase d2 as' -> m (TypeBase d1 as')- matchDims' bound t1 t2 =- case (t1, t2) of- (Array als1 u1 shape1 et1, Array als2 u2 shape2 et2) ->- flip setAliases (als1 <> als2)- <$> ( arrayOf (min u1 u2)- <$> onShapes bound shape1 shape2- <*> matchDims' bound (Scalar et1) (Scalar et2)- )- (Scalar (Record f1), Scalar (Record f2)) ->- Scalar . Record- <$> traverse (uncurry (matchDims' bound)) (M.intersectionWith (,) f1 f2)- (Scalar (Sum cs1), Scalar (Sum cs2)) ->- Scalar . Sum- <$> traverse- (traverse (uncurry (matchDims' bound)))- (M.intersectionWith zip cs1 cs2)- ( Scalar (Arrow als1 p1 d1 a1 (RetType dims1 b1)),- Scalar (Arrow als2 p2 _d2 a2 (RetType dims2 b2))- ) ->- let bound' = mapMaybe paramName [p1, p2] <> dims1 <> dims2 <> bound- in Scalar- <$> ( Arrow (als1 <> als2) p1 d1- <$> matchDims' bound' a1 a2- <*> (RetType dims1 <$> matchDims' bound' b1 b2)- )- ( Scalar (TypeVar als1 u v targs1),- Scalar (TypeVar als2 _ _ targs2)- ) ->- Scalar . TypeVar (als1 <> als2) u v- <$> zipWithM (matchTypeArg bound) targs1 targs2- _ -> pure t1-- matchTypeArg _ ta@TypeArgType {} _ = pure ta- matchTypeArg bound (TypeArgDim x loc) (TypeArgDim y _) =- TypeArgDim <$> onDims bound x y <*> pure loc- matchTypeArg _ a _ = pure a-- onShapes bound shape1 shape2 =- Shape <$> zipWithM (onDims bound) (shapeDims shape1) (shapeDims shape2)---- | Set the uniqueness attribute of a type. If the type is a record--- or sum type, the uniqueness of its components will be modified.-setUniqueness :: TypeBase dim as -> Uniqueness -> TypeBase dim as-setUniqueness (Array als _ shape et) u =- Array als u shape et-setUniqueness (Scalar (TypeVar als _ t targs)) u =- Scalar $ TypeVar als u t targs-setUniqueness (Scalar (Record ets)) u =- Scalar $ Record $ fmap (`setUniqueness` u) ets-setUniqueness (Scalar (Sum ets)) u =- Scalar $ Sum $ fmap (map (`setUniqueness` u)) ets-setUniqueness t _ = t---- | @t \`setAliases\` als@ returns @t@, but with @als@ substituted for--- any already present aliasing.-setAliases :: TypeBase dim asf -> ast -> TypeBase dim ast-setAliases t = addAliases t . const---- | @t \`addAliases\` f@ returns @t@, but with any already present--- aliasing replaced by @f@ applied to that aliasing.-addAliases ::- TypeBase dim asf ->- (asf -> ast) ->- TypeBase dim ast-addAliases = flip second--intValueType :: IntValue -> IntType-intValueType Int8Value {} = Int8-intValueType Int16Value {} = Int16-intValueType Int32Value {} = Int32-intValueType Int64Value {} = Int64--floatValueType :: FloatValue -> FloatType-floatValueType Float16Value {} = Float16-floatValueType Float32Value {} = Float32-floatValueType Float64Value {} = Float64---- | The type of a basic value.-primValueType :: PrimValue -> PrimType-primValueType (SignedValue v) = Signed $ intValueType v-primValueType (UnsignedValue v) = Unsigned $ intValueType v-primValueType (FloatValue v) = FloatType $ floatValueType v-primValueType BoolValue {} = Bool---- | The type of an Futhark term. The aliasing will refer to itself, if--- the term is a non-tuple-typed variable.-typeOf :: ExpBase Info VName -> PatType-typeOf (Literal val _) = Scalar $ Prim $ primValueType val-typeOf (IntLit _ (Info t) _) = t-typeOf (FloatLit _ (Info t) _) = t-typeOf (Parens e _) = typeOf e-typeOf (QualParens _ e _) = typeOf e-typeOf (TupLit es _) = Scalar $ tupleRecord $ map typeOf es-typeOf (RecordLit fs _) =- -- Reverse, because M.unions is biased to the left.- Scalar $ Record $ M.unions $ reverse $ map record fs- where- record (RecordFieldExplicit name e _) = M.singleton name $ typeOf e- record (RecordFieldImplicit name (Info t) _) =- M.singleton (baseName name) $- t- `addAliases` S.insert (AliasBound name)-typeOf (ArrayLit _ (Info t) _) = t-typeOf (StringLit vs _) =- Array- mempty- Nonunique- (Shape [ConstSize $ genericLength vs])- (Prim (Unsigned Int8))-typeOf (Project _ _ (Info t) _) = t-typeOf (Var _ (Info t) _) = t-typeOf (Hole (Info t) _) = t-typeOf (Ascript e _ _) = typeOf e-typeOf (Negate e _) = typeOf e-typeOf (Not e _) = typeOf e-typeOf (Update e _ _ _) = typeOf e `setAliases` mempty-typeOf (RecordUpdate _ _ _ (Info t) _) = t-typeOf (Assert _ e _ _) = typeOf e-typeOf (Lambda params _ _ (Info (als, t)) _) =- funType params t `setAliases` als-typeOf (OpSection _ (Info t) _) =- t-typeOf (OpSectionLeft _ _ _ (_, Info (pn, pt2)) (Info ret, _) _) =- Scalar $ Arrow mempty pn Observe pt2 ret-typeOf (OpSectionRight _ _ _ (Info (pn, pt1), _) (Info ret) _) =- Scalar $ Arrow mempty pn Observe pt1 ret-typeOf (ProjectSection _ (Info t) _) = t-typeOf (IndexSection _ (Info t) _) = t-typeOf (Constr _ _ (Info t) _) = t-typeOf (Attr _ e _) = typeOf e-typeOf (AppExp _ (Info res)) = appResType res---- | The type of a function with the given parameters and return type.-funType :: [PatBase Info VName] -> StructRetType -> StructType-funType params ret =- let RetType _ t = foldr (arrow . patternParam) ret params- in t- where- arrow (xp, d, xt) yt =- RetType [] $ Scalar $ Arrow () xp d xt' yt- where- xt' = xt `setUniqueness` Nonunique---- | @foldFunType ts ret@ creates a function type ('Arrow') that takes--- @ts@ as parameters and returns @ret@.-foldFunType ::- Monoid as =>- [(Diet, TypeBase dim pas)] ->- RetTypeBase dim as ->- TypeBase dim as-foldFunType ps ret =- let RetType _ t = foldr arrow ret ps- in t- where- arrow (d, t1) t2 =- RetType [] $ Scalar $ Arrow mempty Unnamed d t1' t2- where- t1' = toStruct t1 `setUniqueness` Nonunique--foldFunTypeFromParams ::- Monoid as =>- [PatBase Info VName] ->- RetTypeBase Size as ->- TypeBase Size as-foldFunTypeFromParams params =- foldFunType (zip (map diet params_ts) params_ts)- where- params_ts = map patternStructType params---- | Extract the parameter types and return type from a type.--- If the type is not an arrow type, the list of parameter types is empty.-unfoldFunType :: TypeBase dim as -> ([(Diet, TypeBase dim ())], TypeBase dim ())-unfoldFunType (Scalar (Arrow _ _ d t1 (RetType _ t2))) =- let (ps, r) = unfoldFunType t2- in ((d, t1) : ps, r)-unfoldFunType t = ([], toStruct t)---- | The type scheme of a value binding, comprising the type--- parameters and the actual type.-valBindTypeScheme :: ValBindBase Info VName -> ([TypeParamBase VName], StructType)-valBindTypeScheme vb =- ( valBindTypeParams vb,- funType (valBindParams vb) (unInfo (valBindRetType vb))- )---- | The names that are brought into scope by this value binding (not--- including its own parameter names, but including any existential--- sizes).-valBindBound :: ValBindBase Info VName -> [VName]-valBindBound vb =- valBindName vb- : case valBindParams vb of- [] -> retDims (unInfo (valBindRetType vb))- _ -> []---- | The type names mentioned in a type.-typeVars :: Monoid as => TypeBase dim as -> S.Set VName-typeVars t =- case t of- Scalar Prim {} -> mempty- Scalar (TypeVar _ _ tn targs) ->- mconcat $ S.singleton (qualLeaf tn) : map typeArgFree targs- Scalar (Arrow _ _ _ t1 (RetType _ t2)) -> typeVars t1 <> typeVars t2- Scalar (Record fields) -> foldMap typeVars fields- Scalar (Sum cs) -> mconcat $ (foldMap . fmap) typeVars cs- Array _ _ _ rt -> typeVars $ Scalar rt- where- typeArgFree (TypeArgType ta _) = typeVars ta- typeArgFree TypeArgDim {} = mempty---- | @orderZero t@ is 'True' if the argument type has order 0, i.e., it is not--- a function type, does not contain a function type as a subcomponent, and may--- not be instantiated with a function type.-orderZero :: TypeBase dim as -> Bool-orderZero Array {} = True-orderZero (Scalar (Prim _)) = True-orderZero (Scalar (Record fs)) = all orderZero $ M.elems fs-orderZero (Scalar TypeVar {}) = True-orderZero (Scalar Arrow {}) = False-orderZero (Scalar (Sum cs)) = all (all orderZero) cs---- | @patternOrderZero pat@ is 'True' if all of the types in the given pattern--- have order 0.-patternOrderZero :: PatBase Info vn -> Bool-patternOrderZero pat = case pat of- TuplePat ps _ -> all patternOrderZero ps- RecordPat fs _ -> all (patternOrderZero . snd) fs- PatParens p _ -> patternOrderZero p- Id _ (Info t) _ -> orderZero t- Wildcard (Info t) _ -> orderZero t- PatAscription p _ _ -> patternOrderZero p- PatLit _ (Info t) _ -> orderZero t- PatConstr _ _ ps _ -> all patternOrderZero ps- PatAttr _ p _ -> patternOrderZero p---- | The set of identifiers bound in a pattern.-patIdents :: (Functor f, Ord vn) => PatBase f vn -> S.Set (IdentBase f vn)-patIdents (Id v t loc) = S.singleton $ Ident v t loc-patIdents (PatParens p _) = patIdents p-patIdents (TuplePat pats _) = mconcat $ map patIdents pats-patIdents (RecordPat fs _) = mconcat $ map (patIdents . snd) fs-patIdents Wildcard {} = mempty-patIdents (PatAscription p _ _) = patIdents p-patIdents PatLit {} = mempty-patIdents (PatConstr _ _ ps _) = mconcat $ map patIdents ps-patIdents (PatAttr _ p _) = patIdents p---- | The set of names bound in a pattern.-patNames :: (Functor f, Ord vn) => PatBase f vn -> S.Set vn-patNames (Id v _ _) = S.singleton v-patNames (PatParens p _) = patNames p-patNames (TuplePat pats _) = mconcat $ map patNames pats-patNames (RecordPat fs _) = mconcat $ map (patNames . snd) fs-patNames Wildcard {} = mempty-patNames (PatAscription p _ _) = patNames p-patNames PatLit {} = mempty-patNames (PatConstr _ _ ps _) = mconcat $ map patNames ps-patNames (PatAttr _ p _) = patNames p---- | A mapping from names bound in a map to their identifier.-patternMap :: (Functor f) => PatBase f VName -> M.Map VName (IdentBase f VName)-patternMap pat =- M.fromList $ zip (map identName idents) idents- where- idents = S.toList $ patIdents pat---- | The type of values bound by the pattern.-patternType :: PatBase Info VName -> PatType-patternType (Wildcard (Info t) _) = t-patternType (PatParens p _) = patternType p-patternType (Id _ (Info t) _) = t-patternType (TuplePat pats _) = Scalar $ tupleRecord $ map patternType pats-patternType (RecordPat fs _) = Scalar $ Record $ patternType <$> M.fromList fs-patternType (PatAscription p _ _) = patternType p-patternType (PatLit _ (Info t) _) = t-patternType (PatConstr _ (Info t) _ _) = t-patternType (PatAttr _ p _) = patternType p---- | The type matched by the pattern, including shape declarations if present.-patternStructType :: PatBase Info VName -> StructType-patternStructType = toStruct . patternType---- | When viewed as a function parameter, does this pattern correspond--- to a named parameter of some type?-patternParam :: PatBase Info VName -> (PName, Diet, StructType)-patternParam (PatParens p _) =- patternParam p-patternParam (PatAttr _ p _) =- patternParam p-patternParam (PatAscription (Id v (Info t) _) _ _) =- (Named v, diet t, toStruct t)-patternParam (Id v (Info t) _) =- (Named v, diet t, toStruct t)-patternParam p =- (Unnamed, diet p_t, p_t)- where- p_t = patternStructType p---- | Names of primitive types to types. This is only valid if no--- shadowing is going on, but useful for tools.-namesToPrimTypes :: M.Map Name PrimType-namesToPrimTypes =- M.fromList- [ (nameFromString $ prettyString t, t)- | t <-- Bool- : map Signed [minBound .. maxBound]- ++ map Unsigned [minBound .. maxBound]- ++ map FloatType [minBound .. maxBound]- ]---- | The nature of something predefined. For functions, these can--- either be monomorphic or overloaded. An overloaded builtin is a--- list valid types it can be instantiated with, to the parameter and--- result type, with 'Nothing' representing the overloaded parameter--- type.-data Intrinsic- = IntrinsicMonoFun [PrimType] PrimType- | IntrinsicOverloadedFun [PrimType] [Maybe PrimType] (Maybe PrimType)- | IntrinsicPolyFun [TypeParamBase VName] [(Diet, StructType)] (RetTypeBase Size ())- | IntrinsicType Liftedness [TypeParamBase VName] StructType- | IntrinsicEquality -- Special cased.--intrinsicAcc :: (VName, Intrinsic)-intrinsicAcc =- ( acc_v,- IntrinsicType SizeLifted [TypeParamType Unlifted t_v mempty] $- Scalar $- TypeVar () Nonunique (qualName acc_v) [arg]- )- where- acc_v = VName "acc" 10- t_v = VName "t" 11- arg = TypeArgType (Scalar (TypeVar () Nonunique (qualName t_v) [])) mempty---- | A map of all built-ins.-intrinsics :: M.Map VName Intrinsic-intrinsics =- (M.fromList [intrinsicAcc] <>) $- M.fromList $- zipWith namify [20 ..] $- map primFun (M.toList Primitive.primFuns)- ++ map unOpFun Primitive.allUnOps- ++ map binOpFun Primitive.allBinOps- ++ map cmpOpFun Primitive.allCmpOps- ++ map convOpFun Primitive.allConvOps- ++ map signFun Primitive.allIntTypes- ++ map unsignFun Primitive.allIntTypes- ++ map- intrinsicPrim- ( map Signed [minBound .. maxBound]- ++ map Unsigned [minBound .. maxBound]- ++ map FloatType [minBound .. maxBound]- ++ [Bool]- )- ++- -- This overrides the ! from Primitive.- [ ( "!",- IntrinsicOverloadedFun- ( map Signed [minBound .. maxBound]- ++ map Unsigned [minBound .. maxBound]- ++ [Bool]- )- [Nothing]- Nothing- )- ]- ++- -- The reason for the loop formulation is to ensure that we- -- get a missing case warning if we forget a case.- mapMaybe mkIntrinsicBinOp [minBound .. maxBound]- ++ [ ( "flatten",- IntrinsicPolyFun- [tp_a, sp_n, sp_m]- [(Observe, Array () Nonunique (shape [n, m]) t_a)]- $ RetType [k]- $ Array () Nonunique (shape [k]) t_a- ),- ( "unflatten",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, Scalar $ Prim $ Signed Int64),- (Observe, Scalar $ Prim $ Signed Int64),- (Observe, Array () Nonunique (shape [n]) t_a)- ]- $ RetType [k, m]- $ Array () Nonunique (shape [k, m]) t_a- ),- ( "concat",- IntrinsicPolyFun- [tp_a, sp_n, sp_m]- [ (Observe, array_a $ shape [n]),- (Observe, array_a $ shape [m])- ]- $ RetType [k]- $ uarray_a- $ shape [k]- ),- ( "rotate",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, Scalar $ Prim $ Signed Int64),- (Observe, array_a $ shape [n])- ]- $ RetType []- $ array_a- $ shape [n]- ),- ( "transpose",- IntrinsicPolyFun- [tp_a, sp_n, sp_m]- [(Observe, array_a $ shape [n, m])]- $ RetType []- $ array_a- $ shape [m, n]- ),- ( "scatter",- IntrinsicPolyFun- [tp_a, sp_n, sp_l]- [ (Consume, Array () Unique (shape [n]) t_a),- (Observe, Array () Nonunique (shape [l]) (Prim $ Signed Int64)),- (Observe, Array () Nonunique (shape [l]) t_a)- ]- $ RetType []- $ Array () Unique (shape [n]) t_a- ),- ( "scatter_2d",- IntrinsicPolyFun- [tp_a, sp_n, sp_m, sp_l]- [ (Consume, uarray_a $ shape [n, m]),- (Observe, Array () Nonunique (shape [l]) (tupInt64 2)),- (Observe, Array () Nonunique (shape [l]) t_a)- ]- $ RetType []- $ uarray_a- $ shape [n, m]- ),- ( "scatter_3d",- IntrinsicPolyFun- [tp_a, sp_n, sp_m, sp_k, sp_l]- [ (Consume, uarray_a $ shape [n, m, k]),- (Observe, Array () Nonunique (shape [l]) (tupInt64 3)),- (Observe, Array () Nonunique (shape [l]) t_a)- ]- $ RetType []- $ uarray_a- $ shape [n, m, k]- ),- ( "zip",- IntrinsicPolyFun- [tp_a, tp_b, sp_n]- [ (Observe, array_a (shape [n])),- (Observe, array_b (shape [n]))- ]- $ RetType []- $ tuple_uarray (Scalar t_a) (Scalar t_b)- $ shape [n]- ),- ( "unzip",- IntrinsicPolyFun- [tp_a, tp_b, sp_n]- [(Observe, tuple_arr (Scalar t_a) (Scalar t_b) $ shape [n])]- $ RetType [] . Scalar . Record . M.fromList- $ zip tupleFieldNames [array_a $ shape [n], array_b $ shape [n]]- ),- ( "hist_1d",- IntrinsicPolyFun- [tp_a, sp_n, sp_m]- [ (Consume, Scalar $ Prim $ Signed Int64),- (Observe, uarray_a $ shape [m]),- (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- (Observe, Array () Nonunique (shape [n]) (tupInt64 1)),- (Observe, array_a (shape [n]))- ]- $ RetType []- $ uarray_a- $ shape [m]- ),- ( "hist_2d",- IntrinsicPolyFun- [tp_a, sp_n, sp_m, sp_k]- [ (Observe, Scalar $ Prim $ Signed Int64),- (Consume, uarray_a $ shape [m, k]),- (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- (Observe, Array () Nonunique (shape [n]) (tupInt64 2)),- (Observe, array_a (shape [n]))- ]- $ RetType []- $ uarray_a- $ shape [m, k]- ),- ( "hist_3d",- IntrinsicPolyFun- [tp_a, sp_n, sp_m, sp_k, sp_l]- [ (Observe, Scalar $ Prim $ Signed Int64),- (Consume, uarray_a $ shape [m, k, l]),- (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- (Observe, Array () Nonunique (shape [n]) (tupInt64 3)),- (Observe, array_a (shape [n]))- ]- $ RetType []- $ uarray_a- $ shape [m, k, l]- ),- ( "map",- IntrinsicPolyFun- [tp_a, tp_b, sp_n]- [ (Observe, Scalar t_a `arr` Scalar t_b),- (Observe, array_a $ shape [n])- ]- $ RetType []- $ uarray_b- $ shape [n]- ),- ( "reduce",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- (Observe, array_a $ shape [n])- ]- $ RetType []- $ Scalar t_a- ),- ( "reduce_comm",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- (Observe, array_a $ shape [n])- ]- $ RetType []- $ Scalar t_a- ),- ( "scan",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- (Observe, array_a $ shape [n])- ]- $ RetType []- $ uarray_a- $ shape [n]- ),- ( "partition",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, Scalar (Prim $ Signed Int32)),- (Observe, Scalar t_a `arr` Scalar (Prim $ Signed Int64)),- (Observe, array_a $ shape [n])- ]- ( RetType [m] . Scalar $- tupleRecord- [ uarray_a $ shape [k],- Array () Unique (shape [n]) (Prim $ Signed Int64)- ]- )- ),- ( "acc_write",- IntrinsicPolyFun- [sp_k, tp_a]- [ (Consume, Scalar $ accType array_ka),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar t_a)- ]- $ RetType []- $ Scalar- $ accType array_ka- ),- ( "scatter_stream",- IntrinsicPolyFun- [tp_a, tp_b, sp_k, sp_n]- [ (Consume, uarray_ka),- ( Observe,- Scalar (accType array_ka)- `carr` ( Scalar t_b- `arr` Scalar (accType $ array_a $ shape [k])- )- ),- (Observe, array_b $ shape [n])- ]- $ RetType [] uarray_ka- ),- ( "hist_stream",- IntrinsicPolyFun- [tp_a, tp_b, sp_k, sp_n]- [ (Consume, uarray_a $ shape [k]),- (Observe, Scalar t_a `arr` (Scalar t_a `arr` Scalar t_a)),- (Observe, Scalar t_a),- ( Observe,- Scalar (accType array_ka)- `carr` ( Scalar t_b- `arr` Scalar (accType $ array_a $ shape [k])- )- ),- (Observe, array_b $ shape [n])- ]- $ RetType []- $ uarray_a- $ shape [k]- ),- ( "jvp2",- IntrinsicPolyFun- [tp_a, tp_b]- [ (Observe, Scalar t_a `arr` Scalar t_b),- (Observe, Scalar t_a),- (Observe, Scalar t_a)- ]- $ RetType []- $ Scalar- $ tupleRecord [Scalar t_b, Scalar t_b]- ),- ( "vjp2",- IntrinsicPolyFun- [tp_a, tp_b]- [ (Observe, Scalar t_a `arr` Scalar t_b),- (Observe, Scalar t_a),- (Observe, Scalar t_b)- ]- $ RetType []- $ Scalar- $ tupleRecord [Scalar t_b, Scalar t_a]- )- ]- ++- -- Experimental LMAD ones.- [ ( "flat_index_2d",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, array_a $ shape [n]),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64))- ]- $ RetType [m, k]- $ array_a- $ shape [m, k]- ),- ( "flat_update_2d",- IntrinsicPolyFun- [tp_a, sp_n, sp_k, sp_l]- [ (Consume, uarray_a $ shape [n]),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, array_a $ shape [k, l])- ]- $ RetType []- $ uarray_a- $ shape [n]- ),- ( "flat_index_3d",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, array_a $ shape [n]),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64))- ]- $ RetType [m, k, l]- $ array_a- $ shape [m, k, l]- ),- ( "flat_update_3d",- IntrinsicPolyFun- [tp_a, sp_n, sp_k, sp_l, sp_p]- [ (Consume, uarray_a $ shape [n]),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, array_a $ shape [k, l, p])- ]- $ RetType []- $ uarray_a- $ shape [n]- ),- ( "flat_index_4d",- IntrinsicPolyFun- [tp_a, sp_n]- [ (Observe, array_a $ shape [n]),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64))- ]- $ RetType [m, k, l, p]- $ array_a- $ shape [m, k, l, p]- ),- ( "flat_update_4d",- IntrinsicPolyFun- [tp_a, sp_n, sp_k, sp_l, sp_p, sp_q]- [ (Consume, uarray_a $ shape [n]),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, Scalar (Prim $ Signed Int64)),- (Observe, array_a $ shape [k, l, p, q])- ]- $ RetType []- $ uarray_a- $ shape [n]- )- ]- where- [a, b, n, m, k, l, p, q] = zipWith VName (map nameFromString ["a", "b", "n", "m", "k", "l", "p", "q"]) [0 ..]-- t_a = TypeVar () Nonunique (qualName a) []- array_a s = Array () Nonunique s t_a- uarray_a s = Array () Unique s t_a- tp_a = TypeParamType Unlifted a mempty-- t_b = TypeVar () Nonunique (qualName b) []- array_b s = Array () Nonunique s t_b- uarray_b s = Array () Unique s t_b- tp_b = TypeParamType Unlifted b mempty-- [sp_n, sp_m, sp_k, sp_l, sp_p, sp_q] = map (`TypeParamDim` mempty) [n, m, k, l, p, q]-- shape = Shape . map (NamedSize . qualName)-- tuple_arr x y s =- Array- ()- Nonunique- s- (Record (M.fromList $ zip tupleFieldNames [x, y]))- tuple_uarray x y s = tuple_arr x y s `setUniqueness` Unique-- arr x y = Scalar $ Arrow mempty Unnamed Observe x (RetType [] y)- carr x y = Scalar $ Arrow mempty Unnamed Consume x (RetType [] y)-- array_ka = Array () Nonunique (Shape [NamedSize $ qualName k]) t_a- uarray_ka = Array () Unique (Shape [NamedSize $ qualName k]) t_a-- accType t =- TypeVar () Unique (qualName (fst intrinsicAcc)) [TypeArgType t mempty]-- namify i (x, y) = (VName (nameFromString x) i, y)-- primFun (name, (ts, t, _)) =- (name, IntrinsicMonoFun (map unPrim ts) $ unPrim t)-- unOpFun bop = (prettyString bop, IntrinsicMonoFun [t] t)- where- t = unPrim $ Primitive.unOpType bop-- binOpFun bop = (prettyString bop, IntrinsicMonoFun [t, t] t)- where- t = unPrim $ Primitive.binOpType bop-- cmpOpFun bop = (prettyString bop, IntrinsicMonoFun [t, t] Bool)- where- t = unPrim $ Primitive.cmpOpType bop-- convOpFun cop = (prettyString cop, IntrinsicMonoFun [unPrim ft] $ unPrim tt)- where- (ft, tt) = Primitive.convOpType cop-- signFun t = ("sign_" ++ prettyString t, IntrinsicMonoFun [Unsigned t] $ Signed t)-- unsignFun t = ("unsign_" ++ prettyString t, IntrinsicMonoFun [Signed t] $ Unsigned t)-- unPrim (Primitive.IntType t) = Signed t- unPrim (Primitive.FloatType t) = FloatType t- unPrim Primitive.Bool = Bool- unPrim Primitive.Unit = Bool-- intrinsicPrim t = (prettyString t, IntrinsicType Unlifted [] $ Scalar $ Prim t)-- anyIntType =- map Signed [minBound .. maxBound]- ++ map Unsigned [minBound .. maxBound]- anyNumberType =- anyIntType- ++ map FloatType [minBound .. maxBound]- anyPrimType = Bool : anyNumberType-- mkIntrinsicBinOp :: BinOp -> Maybe (String, Intrinsic)- mkIntrinsicBinOp op = do- op' <- intrinsicBinOp op- pure (prettyString op, op')-- binOp ts = Just $ IntrinsicOverloadedFun ts [Nothing, Nothing] Nothing- ordering = Just $ IntrinsicOverloadedFun anyPrimType [Nothing, Nothing] (Just Bool)-- intrinsicBinOp Plus = binOp anyNumberType- intrinsicBinOp Minus = binOp anyNumberType- intrinsicBinOp Pow = binOp anyNumberType- intrinsicBinOp Times = binOp anyNumberType- intrinsicBinOp Divide = binOp anyNumberType- intrinsicBinOp Mod = binOp anyNumberType- intrinsicBinOp Quot = binOp anyIntType- intrinsicBinOp Rem = binOp anyIntType- intrinsicBinOp ShiftR = binOp anyIntType- intrinsicBinOp ShiftL = binOp anyIntType- intrinsicBinOp Band = binOp anyIntType- intrinsicBinOp Xor = binOp anyIntType- intrinsicBinOp Bor = binOp anyIntType- intrinsicBinOp LogAnd = binOp [Bool]- intrinsicBinOp LogOr = binOp [Bool]- intrinsicBinOp Equal = Just IntrinsicEquality- intrinsicBinOp NotEqual = Just IntrinsicEquality- intrinsicBinOp Less = ordering- intrinsicBinOp Leq = ordering- intrinsicBinOp Greater = ordering- intrinsicBinOp Geq = ordering- intrinsicBinOp _ = Nothing-- tupInt64 1 =- Prim $ Signed Int64- tupInt64 x =- tupleRecord $ replicate x $ Scalar $ Prim $ Signed Int64---- | Is this include part of the built-in prelude?-isBuiltin :: FilePath -> Bool-isBuiltin = (== "/prelude") . takeDirectory---- | Is the position of this thing builtin as per 'isBuiltin'? Things--- without location are considered not built-in.-isBuiltinLoc :: Located a => a -> Bool-isBuiltinLoc x =- case locOf x of- NoLoc -> False- Loc pos _ -> isBuiltin $ posFile pos---- | The largest tag used by an intrinsic - this can be used to--- determine whether a 'VName' refers to an intrinsic or a user-defined name.-maxIntrinsicTag :: Int-maxIntrinsicTag = maxinum $ map baseTag $ M.keys intrinsics---- | Create a name with no qualifiers from a name.-qualName :: v -> QualName v-qualName = QualName []---- | Add another qualifier (at the head) to a qualified name.-qualify :: v -> QualName v -> QualName v-qualify k (QualName ks v) = QualName (k : ks) v---- | The modules imported by a Futhark program.-progImports :: ProgBase f vn -> [(String, Loc)]-progImports = concatMap decImports . progDecs---- | The modules imported by a single declaration.-decImports :: DecBase f vn -> [(String, Loc)]-decImports (OpenDec x _) = modExpImports x-decImports (ModDec md) = modExpImports $ modExp md-decImports SigDec {} = []-decImports TypeDec {} = []-decImports ValDec {} = []-decImports (LocalDec d _) = decImports d-decImports (ImportDec x _ loc) = [(x, locOf loc)]--modExpImports :: ModExpBase f vn -> [(String, Loc)]-modExpImports ModVar {} = []-modExpImports (ModParens p _) = modExpImports p-modExpImports (ModImport f _ loc) = [(f, locOf loc)]-modExpImports (ModDecs ds _) = concatMap decImports ds-modExpImports (ModApply _ me _ _ _) = modExpImports me-modExpImports (ModAscript me _ _ _) = modExpImports me-modExpImports ModLambda {} = []---- | The set of module types used in any exported (non-local)--- declaration.-progModuleTypes :: ProgBase Info VName -> S.Set VName-progModuleTypes prog = foldMap reach mtypes_used- where- -- Fixed point iteration.- reach v = S.singleton v <> maybe mempty (foldMap reach) (M.lookup v reachable_from_mtype)-- reachable_from_mtype = foldMap onDec $ progDecs prog- where- onDec OpenDec {} = mempty- onDec ModDec {} = mempty- onDec (SigDec sb) =- M.singleton (sigName sb) (onSigExp (sigExp sb))- onDec TypeDec {} = mempty- onDec ValDec {} = mempty- onDec (LocalDec d _) = onDec d- onDec ImportDec {} = mempty-- onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v- onSigExp (SigParens e _) = onSigExp e- onSigExp (SigSpecs ss _) = foldMap onSpec ss- onSigExp (SigWith e _ _) = onSigExp e- onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2-- onSpec ValSpec {} = mempty- onSpec TypeSpec {} = mempty- onSpec TypeAbbrSpec {} = mempty- onSpec (ModSpec vn e _ _) = S.singleton vn <> onSigExp e- onSpec (IncludeSpec e _) = onSigExp e-- mtypes_used = foldMap onDec $ progDecs prog- where- onDec (OpenDec x _) = onModExp x- onDec (ModDec md) =- maybe mempty (onSigExp . fst) (modSignature md) <> onModExp (modExp md)- onDec SigDec {} = mempty- onDec TypeDec {} = mempty- onDec ValDec {} = mempty- onDec LocalDec {} = mempty- onDec ImportDec {} = mempty-- onModExp ModVar {} = mempty- onModExp (ModParens p _) = onModExp p- onModExp ModImport {} = mempty- onModExp (ModDecs ds _) = mconcat $ map onDec ds- onModExp (ModApply me1 me2 _ _ _) = onModExp me1 <> onModExp me2- onModExp (ModAscript me se _ _) = onModExp me <> onSigExp se- onModExp (ModLambda p r me _) =- onModParam p <> maybe mempty (onSigExp . fst) r <> onModExp me-- onModParam = onSigExp . modParamType-- onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v- onSigExp (SigParens e _) = onSigExp e- onSigExp SigSpecs {} = mempty- onSigExp (SigWith e _ _) = onSigExp e- onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2---- | Extract a leading @((name, namespace, file), remainder)@ from a--- documentation comment string. These are formatted as--- \`name\`\@namespace[\@file]. Let us hope that this pattern does not occur--- anywhere else.-identifierReference :: String -> Maybe ((String, String, Maybe FilePath), String)-identifierReference ('`' : s)- | (identifier, '`' : '@' : s') <- break (== '`') s,- (namespace, s'') <- span isAlpha s',- not $ null namespace =- case s'' of- '@' : '"' : s'''- | (file, '"' : s'''') <- span (/= '"') s''' ->- Just ((identifier, namespace, Just file), s'''')- _ -> Just ((identifier, namespace, Nothing), s'')-identifierReference _ = Nothing---- | Given an operator name, return the operator that determines its--- syntactical properties.-leadingOperator :: Name -> BinOp-leadingOperator s =- maybe Backtick snd $- find ((`isPrefixOf` s') . fst) $- sortOn (Down . length . fst) $- zip (map prettyString operators) operators- where- s' = nameToString s- operators :: [BinOp]- operators = [minBound .. maxBound :: BinOp]---- | Find instances of typed holes in the program.-progHoles :: ProgBase Info VName -> [(Loc, StructType)]-progHoles = foldMap holesInDec . progDecs- where- holesInDec (ValDec vb) = holesInExp $ valBindBody vb- holesInDec (ModDec me) = holesInModExp $ modExp me- holesInDec (OpenDec me _) = holesInModExp me- holesInDec (LocalDec d _) = holesInDec d- holesInDec TypeDec {} = mempty- holesInDec SigDec {} = mempty- holesInDec ImportDec {} = mempty-- holesInModExp (ModDecs ds _) = foldMap holesInDec ds- holesInModExp (ModParens me _) = holesInModExp me- holesInModExp (ModApply x y _ _ _) = holesInModExp x <> holesInModExp y- holesInModExp (ModAscript me _ _ _) = holesInModExp me- holesInModExp (ModLambda _ _ me _) = holesInModExp me- holesInModExp ModVar {} = mempty- holesInModExp ModImport {} = mempty-- holesInExp = flip execState mempty . onExp-- onExp e@(Hole (Info t) loc) = do- modify ((locOf loc, toStruct t) :)- pure e- onExp e = astMap (identityMapper {mapOnExp = onExp}) e---- | A type with no aliasing information but shape annotations.-type UncheckedType = TypeBase (Shape Name) ()---- | An expression with no type annotations.-type UncheckedTypeExp = TypeExp NoInfo Name---- | An identifier with no type annotations.-type UncheckedIdent = IdentBase NoInfo Name---- | An index with no type annotations.-type UncheckedDimIndex = DimIndexBase NoInfo Name---- | A slice with no type annotations.-type UncheckedSlice = SliceBase NoInfo Name---- | An expression with no type annotations.-type UncheckedExp = ExpBase NoInfo Name---- | A module expression with no type annotations.-type UncheckedModExp = ModExpBase NoInfo Name---- | A module type expression with no type annotations.-type UncheckedSigExp = SigExpBase NoInfo Name---- | A type parameter with no type annotations.-type UncheckedTypeParam = TypeParamBase Name---- | A pattern with no type annotations.-type UncheckedPat = PatBase NoInfo Name---- | A function declaration with no type annotations.-type UncheckedValBind = ValBindBase NoInfo Name+ intrinsicVar,+ isBuiltin,+ isBuiltinLoc,+ maxIntrinsicTag,+ namesToPrimTypes,+ qualName,+ qualify,+ primValueType,+ leadingOperator,+ progImports,+ decImports,+ progModuleTypes,+ identifierReference,+ prettyStacktrace,+ progHoles,+ defaultEntryPoint,+ paramName,+ anySize,++ -- * Queries on expressions+ typeOf,+ valBindTypeScheme,+ valBindBound,+ funType,+ stripExp,+ similarExps,++ -- * Queries on patterns and params+ patIdents,+ patNames,+ patternMap,+ patternType,+ patternStructType,+ patternParam,+ patternOrderZero,++ -- * Queries on types+ uniqueness,+ unique,+ diet,+ arrayRank,+ arrayShape,+ orderZero,+ unfoldFunType,+ foldFunType,+ typeVars,+ isAccType,++ -- * Operations on types+ peelArray,+ stripArray,+ arrayOf,+ arrayOfWithAliases,+ toStructural,+ toStruct,+ toRes,+ toParam,+ resToParam,+ paramToRes,+ toResRet,+ setUniqueness,+ noSizes,+ traverseDims,+ DimPos (..),+ tupleRecord,+ isTupleRecord,+ areTupleFields,+ tupleFields,+ tupleFieldNames,+ sortFields,+ sortConstrs,+ isTypeParam,+ isSizeParam,+ matchDims,++ -- * Un-typechecked ASTs+ UncheckedType,+ UncheckedTypeExp,+ UncheckedIdent,+ UncheckedDimIndex,+ UncheckedSlice,+ UncheckedExp,+ UncheckedModExp,+ UncheckedSigExp,+ UncheckedTypeParam,+ UncheckedPat,+ UncheckedValBind,+ UncheckedTypeBind,+ UncheckedSigBind,+ UncheckedModBind,+ UncheckedDec,+ UncheckedSpec,+ UncheckedProg,+ UncheckedCase,++ -- * Type-checked ASTs+ Ident,+ DimIndex,+ Slice,+ AppExp,+ Exp,+ Pat,+ ModExp,+ ModParam,+ SigExp,+ ModBind,+ SigBind,+ ValBind,+ Dec,+ Spec,+ Prog,+ TypeBind,+ StructTypeArg,+ ScalarType,+ TypeParam,+ Case,+ )+where++import Control.Monad+import Control.Monad.State+import Data.Bifunctor+import Data.Bitraversable (bitraverse)+import Data.Char+import Data.Foldable+import Data.List (genericLength, isPrefixOf, sortOn)+import Data.List.NonEmpty qualified as NE+import Data.Loc (Loc (..), posFile)+import Data.Map.Strict qualified as M+import Data.Maybe+import Data.Ord+import Data.Set qualified as S+import Futhark.Util (maxinum)+import Futhark.Util.Pretty+import Language.Futhark.Primitive qualified as Primitive+import Language.Futhark.Syntax+import Language.Futhark.Traversals+import Language.Futhark.Tuple+import System.FilePath (takeDirectory)++-- | The name of the default program entry point (@main@).+defaultEntryPoint :: Name+defaultEntryPoint = nameFromString "main"++-- | Return the dimensionality of a type. For non-arrays, this is+-- zero. For a one-dimensional array it is one, for a two-dimensional+-- it is two, and so forth.+arrayRank :: TypeBase dim as -> Int+arrayRank = shapeRank . arrayShape++-- | Return the shape of a type - for non-arrays, this is 'mempty'.+arrayShape :: TypeBase dim as -> Shape dim+arrayShape (Array _ ds _) = ds+arrayShape _ = mempty++-- | Change the shape of a type to be just the rank.+noSizes :: TypeBase Size as -> TypeBase () as+noSizes = first $ const ()++-- | Where does this dimension occur?+data DimPos+ = -- | Immediately in the argument to 'traverseDims'.+ PosImmediate+ | -- | In a function parameter type.+ PosParam+ | -- | In a function return type.+ PosReturn+ deriving (Eq, Ord, Show)++-- | Perform a traversal (possibly including replacement) on sizes+-- that are parameters in a function type, but also including the type+-- immediately passed to the function. Also passes along a set of the+-- parameter names inside the type that have come in scope at the+-- occurrence of the dimension.+traverseDims ::+ forall f fdim tdim als.+ Applicative f =>+ (S.Set VName -> DimPos -> fdim -> f tdim) ->+ TypeBase fdim als ->+ f (TypeBase tdim als)+traverseDims f = go mempty PosImmediate+ where+ go ::+ forall als'.+ S.Set VName ->+ DimPos ->+ TypeBase fdim als' ->+ f (TypeBase tdim als')+ go bound b t@Array {} =+ bitraverse (f bound b) pure t+ go bound b (Scalar (Record fields)) =+ Scalar . Record <$> traverse (go bound b) fields+ go bound b (Scalar (TypeVar as tn targs)) =+ Scalar <$> (TypeVar as tn <$> traverse (onTypeArg bound b) targs)+ go bound b (Scalar (Sum cs)) =+ Scalar . Sum <$> traverse (traverse (go bound b)) cs+ go _ _ (Scalar (Prim t)) =+ pure $ Scalar $ Prim t+ go bound _ (Scalar (Arrow als p u t1 (RetType dims t2))) =+ Scalar <$> (Arrow als p u <$> go bound' PosParam t1 <*> (RetType dims <$> go bound' PosReturn t2))+ where+ bound' =+ S.fromList dims+ <> case p of+ Named p' -> S.insert p' bound+ Unnamed -> bound++ onTypeArg bound b (TypeArgDim d) =+ TypeArgDim <$> f bound b d+ onTypeArg bound b (TypeArgType t) =+ TypeArgType <$> go bound b t++-- | Return the uniqueness of a type.+uniqueness :: TypeBase shape Uniqueness -> Uniqueness+uniqueness (Array u _ _) = u+uniqueness (Scalar (TypeVar u _ _)) = u+uniqueness (Scalar (Sum ts))+ | any (any unique) ts = Unique+uniqueness (Scalar (Record fs))+ | any unique fs = Unique+uniqueness _ = Nonunique++-- | @unique t@ is 'True' if the type of the argument is unique.+unique :: TypeBase shape Uniqueness -> Bool+unique = (== Unique) . uniqueness++-- | @diet t@ returns a description of how a function parameter of+-- type @t@ consumes its argument.+diet :: TypeBase shape Diet -> Diet+diet (Scalar (Record ets)) = foldl max Observe $ fmap diet ets+diet (Scalar (Prim _)) = Observe+diet (Scalar (Arrow {})) = Observe+diet (Array d _ _) = d+diet (Scalar (TypeVar d _ _)) = d+diet (Scalar (Sum cs)) = foldl max Observe $ foldMap (map diet) cs++-- | Convert any type to one that has rank information, no alias+-- information, and no embedded names.+toStructural ::+ TypeBase dim as ->+ TypeBase () ()+toStructural = bimap (const ()) (const ())++-- | Remove uniquenss information from a type.+toStruct :: TypeBase dim u -> TypeBase dim NoUniqueness+toStruct = second (const NoUniqueness)++-- | Uses 'Observe'.+toParam :: Diet -> TypeBase Size u -> ParamType+toParam d = fmap (const d)++-- | Convert to 'ResType'+toRes :: Uniqueness -> TypeBase Size u -> ResType+toRes u = fmap (const u)++-- | Convert to 'ResRetType'+toResRet :: Uniqueness -> RetTypeBase Size u -> ResRetType+toResRet u = second (const u)++-- | Preserves relation between 'Diet' and 'Uniqueness'.+resToParam :: ResType -> ParamType+resToParam = second f+ where+ f Unique = Consume+ f Nonunique = Observe++-- | Preserves relation between 'Diet' and 'Uniqueness'.+paramToRes :: ParamType -> ResType+paramToRes = second f+ where+ f Consume = Unique+ f Observe = Nonunique++-- | @peelArray n t@ returns the type resulting from peeling the first+-- @n@ array dimensions from @t@. Returns @Nothing@ if @t@ has less+-- than @n@ dimensions.+peelArray :: Int -> TypeBase dim u -> Maybe (TypeBase dim u)+peelArray n (Array u shape t)+ | shapeRank shape == n =+ Just $ second (const u) (Scalar t)+ | otherwise =+ Array u <$> stripDims n shape <*> pure t+peelArray _ _ = Nothing++-- | @arrayOf u s t@ constructs an array type. The convenience+-- compared to using the 'Array' constructor directly is that @t@ can+-- itself be an array. If @t@ is an @n@-dimensional array, and @s@ is+-- a list of length @n@, the resulting type is of an @n+m@ dimensions.+arrayOf ::+ Shape dim ->+ TypeBase dim NoUniqueness ->+ TypeBase dim NoUniqueness+arrayOf = arrayOfWithAliases mempty++-- | Like 'arrayOf', but you can pass in uniqueness info of the+-- resulting array.+arrayOfWithAliases ::+ u ->+ Shape dim ->+ TypeBase dim u ->+ TypeBase dim u+arrayOfWithAliases u shape2 (Array _ shape1 et) =+ Array u (shape2 <> shape1) et+arrayOfWithAliases u shape (Scalar t) =+ Array u shape (second (const mempty) t)++-- | @stripArray n t@ removes the @n@ outermost layers of the array.+-- Essentially, it is the type of indexing an array of type @t@ with+-- @n@ indexes.+stripArray :: Int -> TypeBase dim as -> TypeBase dim as+stripArray n (Array u shape et)+ | Just shape' <- stripDims n shape =+ Array u shape' et+ | otherwise =+ second (const u) (Scalar et)+stripArray _ t = t++-- | Create a record type corresponding to a tuple with the given+-- element types.+tupleRecord :: [TypeBase dim as] -> ScalarTypeBase dim as+tupleRecord = Record . M.fromList . zip tupleFieldNames++-- | Does this type corespond to a tuple? If so, return the elements+-- of that tuple.+isTupleRecord :: TypeBase dim as -> Maybe [TypeBase dim as]+isTupleRecord (Scalar (Record fs)) = areTupleFields fs+isTupleRecord _ = Nothing++-- | Sort the constructors of a sum type in some well-defined (but not+-- otherwise significant) manner.+sortConstrs :: M.Map Name a -> [(Name, a)]+sortConstrs cs = sortOn fst $ M.toList cs++-- | Is this a 'TypeParamType'?+isTypeParam :: TypeParamBase vn -> Bool+isTypeParam TypeParamType {} = True+isTypeParam TypeParamDim {} = False++-- | Is this a 'TypeParamDim'?+isSizeParam :: TypeParamBase vn -> Bool+isSizeParam = not . isTypeParam++-- | The name, if any.+paramName :: PName -> Maybe VName+paramName (Named v) = Just v+paramName Unnamed = Nothing++-- | A special expression representing no known size. When present in+-- a type, each instance represents a distinct size. The type checker+-- should _never_ produce these - they are a (hopefully temporary)+-- thing introduced by defunctorisation and monomorphisation. They+-- represent a flaw in our implementation. When they occur in a+-- return type, they can be replaced with freshly created existential+-- sizes. When they occur in parameter types, they can be replaced+-- with size parameters.+anySize :: Size+anySize =+ -- The definition here is weird to avoid seeing this as a free+ -- variable.+ StringLit [65, 78, 89] mempty++-- | Match the dimensions of otherwise assumed-equal types. The+-- combining function is also passed the names bound within the type+-- (from named parameters or return types).+matchDims ::+ forall as m d1 d2.+ (Monoid as, Monad m) =>+ ([VName] -> d1 -> d2 -> m d1) ->+ TypeBase d1 as ->+ TypeBase d2 as ->+ m (TypeBase d1 as)+matchDims onDims = matchDims' mempty+ where+ matchDims' ::+ forall u'. Monoid u' => [VName] -> TypeBase d1 u' -> TypeBase d2 u' -> m (TypeBase d1 u')+ matchDims' bound t1 t2 =+ case (t1, t2) of+ (Array u1 shape1 et1, Array u2 shape2 et2) ->+ arrayOfWithAliases u1+ <$> onShapes bound shape1 shape2+ <*> matchDims' bound (second (const u2) (Scalar et1)) (second (const u2) (Scalar et2))+ (Scalar (Record f1), Scalar (Record f2)) ->+ Scalar . Record+ <$> traverse (uncurry (matchDims' bound)) (M.intersectionWith (,) f1 f2)+ (Scalar (Sum cs1), Scalar (Sum cs2)) ->+ Scalar . Sum+ <$> traverse+ (traverse (uncurry (matchDims' bound)))+ (M.intersectionWith zip cs1 cs2)+ ( Scalar (Arrow als1 p1 d1 a1 (RetType dims1 b1)),+ Scalar (Arrow als2 p2 _d2 a2 (RetType dims2 b2))+ ) ->+ let bound' = mapMaybe paramName [p1, p2] <> dims1 <> dims2 <> bound+ in Scalar+ <$> ( Arrow (als1 <> als2) p1 d1+ <$> matchDims' bound' a1 a2+ <*> (RetType dims1 <$> matchDims' bound' b1 b2)+ )+ ( Scalar (TypeVar als1 v targs1),+ Scalar (TypeVar als2 _ targs2)+ ) ->+ Scalar . TypeVar (als1 <> als2) v+ <$> zipWithM (matchTypeArg bound) targs1 targs2+ _ -> pure t1++ matchTypeArg _ ta@TypeArgType {} _ = pure ta+ matchTypeArg bound (TypeArgDim x) (TypeArgDim y) =+ TypeArgDim <$> onDims bound x y+ matchTypeArg _ a _ = pure a++ onShapes bound shape1 shape2 =+ Shape <$> zipWithM (onDims bound) (shapeDims shape1) (shapeDims shape2)++-- | Set the uniqueness attribute of a type. If the type is a record+-- or sum type, the uniqueness of its components will be modified.+setUniqueness :: TypeBase dim u1 -> u2 -> TypeBase dim u2+setUniqueness t u = second (const u) t++intValueType :: IntValue -> IntType+intValueType Int8Value {} = Int8+intValueType Int16Value {} = Int16+intValueType Int32Value {} = Int32+intValueType Int64Value {} = Int64++floatValueType :: FloatValue -> FloatType+floatValueType Float16Value {} = Float16+floatValueType Float32Value {} = Float32+floatValueType Float64Value {} = Float64++-- | The type of a basic value.+primValueType :: PrimValue -> PrimType+primValueType (SignedValue v) = Signed $ intValueType v+primValueType (UnsignedValue v) = Unsigned $ intValueType v+primValueType (FloatValue v) = FloatType $ floatValueType v+primValueType BoolValue {} = Bool++-- | The type of an Futhark term. The aliasing will refer to itself, if+-- the term is a non-tuple-typed variable.+typeOf :: ExpBase Info VName -> StructType+typeOf (Literal val _) = Scalar $ Prim $ primValueType val+typeOf (IntLit _ (Info t) _) = t+typeOf (FloatLit _ (Info t) _) = t+typeOf (Parens e _) = typeOf e+typeOf (QualParens _ e _) = typeOf e+typeOf (TupLit es _) = Scalar $ tupleRecord $ map typeOf es+typeOf (RecordLit fs _) =+ Scalar $ Record $ M.fromList $ map record fs+ where+ record (RecordFieldExplicit name e _) = (name, typeOf e)+ record (RecordFieldImplicit name (Info t) _) = (baseName name, t)+typeOf (ArrayLit _ (Info t) _) = t+typeOf (StringLit vs loc) =+ Array+ mempty+ (Shape [sizeFromInteger (genericLength vs) loc])+ (Prim (Unsigned Int8))+typeOf (Project _ _ (Info t) _) = t+typeOf (Var _ (Info t) _) = t+typeOf (Hole (Info t) _) = t+typeOf (Ascript e _ _) = typeOf e+typeOf (Coerce _ _ (Info t) _) = t+typeOf (Negate e _) = typeOf e+typeOf (Not e _) = typeOf e+typeOf (Update e _ _ _) = typeOf e+typeOf (RecordUpdate _ _ _ (Info t) _) = t+typeOf (Assert _ e _ _) = typeOf e+typeOf (Lambda params _ _ (Info t) _) = funType params t+typeOf (OpSection _ (Info t) _) = t+typeOf (OpSectionLeft _ _ _ (_, Info (pn, pt2)) (Info ret, _) _) =+ Scalar $ Arrow mempty pn (diet pt2) (toStruct pt2) ret+typeOf (OpSectionRight _ _ _ (Info (pn, pt1), _) (Info ret) _) =+ Scalar $ Arrow mempty pn (diet pt1) (toStruct pt1) ret+typeOf (ProjectSection _ (Info t) _) = t+typeOf (IndexSection _ (Info t) _) = t+typeOf (Constr _ _ (Info t) _) = t+typeOf (Attr _ e _) = typeOf e+typeOf (AppExp _ (Info res)) = appResType res++-- | The type of a function with the given parameters and return type.+funType :: [Pat ParamType] -> ResRetType -> StructType+funType params ret =+ let RetType _ t = foldr (arrow . patternParam) ret params+ in toStruct t+ where+ arrow (xp, d, xt) yt =+ RetType [] $ Scalar $ Arrow Nonunique xp d xt yt++-- | @foldFunType ts ret@ creates a function type ('Arrow') that takes+-- @ts@ as parameters and returns @ret@.+foldFunType :: [ParamType] -> ResRetType -> StructType+foldFunType ps ret =+ let RetType _ t = foldr arrow ret ps+ in toStruct t+ where+ arrow t1 t2 =+ RetType [] $ Scalar $ Arrow Nonunique Unnamed (diet t1) (toStruct t1) t2++-- | Extract the parameter types and return type from a type.+-- If the type is not an arrow type, the list of parameter types is empty.+unfoldFunType :: TypeBase dim as -> ([TypeBase dim Diet], TypeBase dim NoUniqueness)+unfoldFunType (Scalar (Arrow _ _ d t1 (RetType _ t2))) =+ let (ps, r) = unfoldFunType t2+ in (second (const d) t1 : ps, r)+unfoldFunType t = ([], toStruct t)++-- | The type scheme of a value binding, comprising the type+-- parameters and the actual type.+valBindTypeScheme :: ValBindBase Info VName -> ([TypeParamBase VName], StructType)+valBindTypeScheme vb =+ ( valBindTypeParams vb,+ funType (valBindParams vb) (unInfo (valBindRetType vb))+ )++-- | The names that are brought into scope by this value binding (not+-- including its own parameter names, but including any existential+-- sizes).+valBindBound :: ValBindBase Info VName -> [VName]+valBindBound vb =+ valBindName vb+ : case valBindParams vb of+ [] -> retDims (unInfo (valBindRetType vb))+ _ -> []++-- | The type names mentioned in a type.+typeVars :: TypeBase dim as -> S.Set VName+typeVars t =+ case t of+ Scalar Prim {} -> mempty+ Scalar (TypeVar _ tn targs) ->+ mconcat $ S.singleton (qualLeaf tn) : map typeArgFree targs+ Scalar (Arrow _ _ _ t1 (RetType _ t2)) -> typeVars t1 <> typeVars t2+ Scalar (Record fields) -> foldMap typeVars fields+ Scalar (Sum cs) -> mconcat $ (foldMap . fmap) typeVars cs+ Array _ _ rt -> typeVars $ Scalar rt+ where+ typeArgFree (TypeArgType ta) = typeVars ta+ typeArgFree TypeArgDim {} = mempty++-- | @orderZero t@ is 'True' if the argument type has order 0, i.e., it is not+-- a function type, does not contain a function type as a subcomponent, and may+-- not be instantiated with a function type.+orderZero :: TypeBase dim as -> Bool+orderZero Array {} = True+orderZero (Scalar (Prim _)) = True+orderZero (Scalar (Record fs)) = all orderZero $ M.elems fs+orderZero (Scalar TypeVar {}) = True+orderZero (Scalar Arrow {}) = False+orderZero (Scalar (Sum cs)) = all (all orderZero) cs++-- | @patternOrderZero pat@ is 'True' if all of the types in the given pattern+-- have order 0.+patternOrderZero :: Pat (TypeBase d u) -> Bool+patternOrderZero = orderZero . patternType++-- | The set of identifiers bound in a pattern.+patIdents :: Pat t -> [Ident t]+patIdents (Id v t loc) = [Ident v t loc]+patIdents (PatParens p _) = patIdents p+patIdents (TuplePat pats _) = foldMap patIdents pats+patIdents (RecordPat fs _) = foldMap (patIdents . snd) fs+patIdents Wildcard {} = mempty+patIdents (PatAscription p _ _) = patIdents p+patIdents PatLit {} = mempty+patIdents (PatConstr _ _ ps _) = foldMap patIdents ps+patIdents (PatAttr _ p _) = patIdents p++-- | The set of names bound in a pattern.+patNames :: Pat t -> [VName]+patNames = map fst . patternMap++-- | Each name bound in a pattern alongside its type.+patternMap :: Pat t -> [(VName, t)]+patternMap = map f . patIdents+ where+ f (Ident v (Info t) _) = (v, t)++-- | The type of values bound by the pattern.+patternType :: Pat (TypeBase d u) -> TypeBase d u+patternType (Wildcard (Info t) _) = t+patternType (PatParens p _) = patternType p+patternType (Id _ (Info t) _) = t+patternType (TuplePat pats _) = Scalar $ tupleRecord $ map patternType pats+patternType (RecordPat fs _) = Scalar $ Record $ patternType <$> M.fromList fs+patternType (PatAscription p _ _) = patternType p+patternType (PatLit _ (Info t) _) = t+patternType (PatConstr _ (Info t) _ _) = t+patternType (PatAttr _ p _) = patternType p++-- | The type matched by the pattern, including shape declarations if present.+patternStructType :: Pat (TypeBase Size u) -> StructType+patternStructType = toStruct . patternType++-- | When viewed as a function parameter, does this pattern correspond+-- to a named parameter of some type?+patternParam :: Pat ParamType -> (PName, Diet, StructType)+patternParam (PatParens p _) =+ patternParam p+patternParam (PatAttr _ p _) =+ patternParam p+patternParam (PatAscription (Id v (Info t) _) _ _) =+ (Named v, diet t, toStruct t)+patternParam (Id v (Info t) _) =+ (Named v, diet t, toStruct t)+patternParam p =+ (Unnamed, diet p_t, toStruct p_t)+ where+ p_t = patternType p++-- | Names of primitive types to types. This is only valid if no+-- shadowing is going on, but useful for tools.+namesToPrimTypes :: M.Map Name PrimType+namesToPrimTypes =+ M.fromList+ [ (nameFromString $ prettyString t, t)+ | t <-+ Bool+ : map Signed [minBound .. maxBound]+ ++ map Unsigned [minBound .. maxBound]+ ++ map FloatType [minBound .. maxBound]+ ]++-- | The nature of something predefined. For functions, these can+-- either be monomorphic or overloaded. An overloaded builtin is a+-- list valid types it can be instantiated with, to the parameter and+-- result type, with 'Nothing' representing the overloaded parameter+-- type.+data Intrinsic+ = IntrinsicMonoFun [PrimType] PrimType+ | IntrinsicOverloadedFun [PrimType] [Maybe PrimType] (Maybe PrimType)+ | IntrinsicPolyFun [TypeParamBase VName] [ParamType] (RetTypeBase Size Uniqueness)+ | IntrinsicType Liftedness [TypeParamBase VName] StructType+ | IntrinsicEquality -- Special cased.++intrinsicAcc :: (VName, Intrinsic)+intrinsicAcc =+ ( acc_v,+ IntrinsicType SizeLifted [TypeParamType Unlifted t_v mempty] $+ Scalar $+ TypeVar mempty (qualName acc_v) [arg]+ )+ where+ acc_v = VName "acc" 10+ t_v = VName "t" 11+ arg = TypeArgType $ Scalar (TypeVar mempty (qualName t_v) [])++-- | If this type corresponds to the builtin "acc" type, return the+-- type of the underlying array.+isAccType :: TypeBase d u -> Maybe (TypeBase d NoUniqueness)+isAccType (Scalar (TypeVar _ (QualName [] v) [TypeArgType t]))+ | v == fst intrinsicAcc =+ Just t+isAccType _ = Nothing++-- | Find the 'VName' corresponding to a builtin. Crashes if that+-- name cannot be found.+intrinsicVar :: Name -> VName+intrinsicVar v =+ fromMaybe bad $ find ((v ==) . baseName) $ M.keys intrinsics+ where+ bad = error $ "findBuiltin: " <> nameToString v++mkBinOp :: Name -> StructType -> Exp -> Exp -> Exp+mkBinOp op t x y =+ AppExp+ ( BinOp+ (qualName (intrinsicVar op), mempty)+ (Info t)+ (x, Info Nothing)+ (y, Info Nothing)+ mempty+ )+ (Info $ AppRes t [])++mkAdd, mkMul :: Exp -> Exp -> Exp+mkAdd = mkBinOp "+" $ Scalar $ Prim $ Signed Int64+mkMul = mkBinOp "*" $ Scalar $ Prim $ Signed Int64++-- | A map of all built-ins.+intrinsics :: M.Map VName Intrinsic+intrinsics =+ (M.fromList [intrinsicAcc] <>) $+ M.fromList $+ primOp+ ++ zipWith+ namify+ [intrinsicStart ..]+ ( [ ( "flatten",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m]+ [Array Observe (shape [n, m]) $ t_a mempty]+ $ RetType []+ $ Array+ Nonunique+ (Shape [size n `mkMul` size m])+ (t_a mempty)+ ),+ ( "unflatten",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m]+ [ Scalar $ Prim $ Signed Int64,+ Scalar $ Prim $ Signed Int64,+ Array Observe (Shape [size n `mkMul` size m]) $ t_a mempty+ ]+ $ RetType []+ $ Array Nonunique (shape [n, m]) (t_a mempty)+ ),+ ( "concat",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m]+ [ array_a Observe $ shape [n],+ array_a Observe $ shape [m]+ ]+ $ RetType []+ $ array_a Unique+ $ Shape [size n `mkAdd` size m]+ ),+ ( "transpose",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m]+ [array_a Observe $ shape [n, m]]+ $ RetType []+ $ array_a Nonunique+ $ shape [m, n]+ ),+ ( "scatter",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_l]+ [ Array Consume (shape [n]) $ t_a mempty,+ Array Observe (shape [l]) (Prim $ Signed Int64),+ Array Observe (shape [l]) $ t_a mempty+ ]+ $ RetType []+ $ Array Unique (shape [n]) (t_a mempty)+ ),+ ( "scatter_2d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m, sp_l]+ [ array_a Consume $ shape [n, m],+ Array Observe (shape [l]) (tupInt64 2),+ Array Observe (shape [l]) $ t_a mempty+ ]+ $ RetType []+ $ array_a Unique+ $ shape [n, m]+ ),+ ( "scatter_3d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m, sp_k, sp_l]+ [ array_a Consume $ shape [n, m, k],+ Array Observe (shape [l]) (tupInt64 3),+ Array Observe (shape [l]) $ t_a mempty+ ]+ $ RetType []+ $ array_a Unique+ $ shape [n, m, k]+ ),+ ( "zip",+ IntrinsicPolyFun+ [tp_a, tp_b, sp_n]+ [ array_a Observe (shape [n]),+ array_b Observe (shape [n])+ ]+ $ RetType []+ $ tuple_array Unique (Scalar $ t_a mempty) (Scalar $ t_b mempty)+ $ shape [n]+ ),+ ( "unzip",+ IntrinsicPolyFun+ [tp_a, tp_b, sp_n]+ [tuple_array Observe (Scalar $ t_a mempty) (Scalar $ t_b mempty) $ shape [n]]+ $ RetType [] . Scalar . Record . M.fromList+ $ zip tupleFieldNames [array_a Unique $ shape [n], array_b Unique $ shape [n]]+ ),+ ( "hist_1d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m]+ [ Scalar $ Prim $ Signed Int64,+ array_a Consume $ shape [m],+ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ Array Observe (shape [n]) (tupInt64 1),+ array_a Observe (shape [n])+ ]+ $ RetType []+ $ array_a Unique+ $ shape [m]+ ),+ ( "hist_2d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m, sp_k]+ [ Scalar $ Prim $ Signed Int64,+ array_a Consume $ shape [m, k],+ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ Array Observe (shape [n]) (tupInt64 2),+ array_a Observe (shape [n])+ ]+ $ RetType []+ $ array_a Unique+ $ shape [m, k]+ ),+ ( "hist_3d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_m, sp_k, sp_l]+ [ Scalar $ Prim $ Signed Int64,+ array_a Consume $ shape [m, k, l],+ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ Array Observe (shape [n]) (tupInt64 3),+ array_a Observe (shape [n])+ ]+ $ RetType []+ $ array_a Unique+ $ shape [m, k, l]+ ),+ ( "map",+ IntrinsicPolyFun+ [tp_a, tp_b, sp_n]+ [ Scalar (t_a mempty) `arr` Scalar (t_b Nonunique),+ array_a Observe $ shape [n]+ ]+ $ RetType []+ $ array_b Unique+ $ shape [n]+ ),+ ( "reduce",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ array_a Observe $ shape [n]+ ]+ $ RetType []+ $ Scalar (t_a Unique)+ ),+ ( "reduce_comm",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ array_a Observe $ shape [n]+ ]+ $ RetType [] (Scalar (t_a Unique))+ ),+ ( "scan",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ array_a Observe $ shape [n]+ ]+ $ RetType [] (array_a Unique $ shape [n])+ ),+ ( "partition",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ Scalar (Prim $ Signed Int32),+ Scalar (t_a mempty) `arr` Scalar (Prim $ Signed Int64),+ array_a Observe $ shape [n]+ ]+ ( RetType [k] . Scalar $+ tupleRecord+ [ array_a Unique $ shape [n],+ Array Unique (shape [k]) (Prim $ Signed Int64)+ ]+ )+ ),+ ( "acc_write",+ IntrinsicPolyFun+ [sp_k, tp_a]+ [ Scalar $ accType Consume $ array_ka mempty,+ Scalar (Prim $ Signed Int64),+ Scalar $ t_a Observe+ ]+ $ RetType []+ $ Scalar+ $ accType Unique (array_ka mempty)+ ),+ ( "scatter_stream",+ IntrinsicPolyFun+ [tp_a, tp_b, sp_k, sp_n]+ [ array_ka Consume,+ Scalar (accType mempty (array_ka mempty))+ `carr` ( Scalar (t_b mempty)+ `arr` Scalar (accType Nonunique $ array_a mempty $ shape [k])+ ),+ array_b Observe $ shape [n]+ ]+ $ RetType []+ $ array_ka Unique+ ),+ ( "hist_stream",+ IntrinsicPolyFun+ [tp_a, tp_b, sp_k, sp_n]+ [ array_a Consume $ shape [k],+ Scalar (t_a mempty) `arr` (Scalar (t_a mempty) `arr` Scalar (t_a Nonunique)),+ Scalar $ t_a Observe,+ Scalar (accType mempty $ array_ka mempty)+ `carr` ( Scalar (t_b mempty)+ `arr` Scalar (accType Nonunique $ array_a mempty $ shape [k])+ ),+ array_b Observe $ shape [n]+ ]+ $ RetType []+ $ array_a Unique+ $ shape [k]+ ),+ ( "jvp2",+ IntrinsicPolyFun+ [tp_a, tp_b]+ [ Scalar (t_a mempty) `arr` Scalar (t_b Nonunique),+ Scalar (t_a Observe),+ Scalar (t_a Observe)+ ]+ $ RetType []+ $ Scalar+ $ tupleRecord [Scalar $ t_b Nonunique, Scalar $ t_b Nonunique]+ ),+ ( "vjp2",+ IntrinsicPolyFun+ [tp_a, tp_b]+ [ Scalar (t_a mempty) `arr` Scalar (t_b Nonunique),+ Scalar (t_a Observe),+ Scalar (t_b Observe)+ ]+ $ RetType []+ $ Scalar+ $ tupleRecord [Scalar $ t_b Nonunique, Scalar $ t_a Nonunique]+ )+ ]+ +++ -- Experimental LMAD ones.+ [ ( "flat_index_2d",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ array_a Observe $ shape [n],+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64)+ ]+ $ RetType [m, k]+ $ array_a Nonunique+ $ shape [m, k]+ ),+ ( "flat_update_2d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_k, sp_l]+ [ array_a Consume $ shape [n],+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ array_a Observe $ shape [k, l]+ ]+ $ RetType []+ $ array_a Unique+ $ shape [n]+ ),+ ( "flat_index_3d",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ array_a Observe $ shape [n],+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64)+ ]+ $ RetType [m, k, l]+ $ array_a Nonunique+ $ shape [m, k, l]+ ),+ ( "flat_update_3d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_k, sp_l, sp_p]+ [ array_a Consume $ shape [n],+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ array_a Observe $ shape [k, l, p]+ ]+ $ RetType []+ $ array_a Unique+ $ shape [n]+ ),+ ( "flat_index_4d",+ IntrinsicPolyFun+ [tp_a, sp_n]+ [ array_a Observe $ shape [n],+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64)+ ]+ $ RetType [m, k, l, p]+ $ array_a Nonunique+ $ shape [m, k, l, p]+ ),+ ( "flat_update_4d",+ IntrinsicPolyFun+ [tp_a, sp_n, sp_k, sp_l, sp_p, sp_q]+ [ array_a Consume $ shape [n],+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ Scalar (Prim $ Signed Int64),+ array_a Observe $ shape [k, l, p, q]+ ]+ $ RetType []+ $ array_a Unique+ $ shape [n]+ )+ ]+ )+ where+ primOp =+ zipWith namify [20 ..] $+ map primFun (M.toList Primitive.primFuns)+ ++ map unOpFun Primitive.allUnOps+ ++ map binOpFun Primitive.allBinOps+ ++ map cmpOpFun Primitive.allCmpOps+ ++ map convOpFun Primitive.allConvOps+ ++ map signFun Primitive.allIntTypes+ ++ map unsignFun Primitive.allIntTypes+ ++ map+ intrinsicPrim+ ( map Signed [minBound .. maxBound]+ ++ map Unsigned [minBound .. maxBound]+ ++ map FloatType [minBound .. maxBound]+ ++ [Bool]+ )+ +++ -- This overrides the ! from Primitive.++ -- This overrides the ! from Primitive.+ [ ( "!",+ IntrinsicOverloadedFun+ ( map Signed [minBound .. maxBound]+ ++ map Unsigned [minBound .. maxBound]+ ++ [Bool]+ )+ [Nothing]+ Nothing+ )+ ]+ +++ -- The reason for the loop formulation is to ensure that we+ -- get a missing case warning if we forget a case.+ mapMaybe mkIntrinsicBinOp [minBound .. maxBound]++ intrinsicStart = 1 + baseTag (fst $ last primOp)++ [a, b, n, m, k, l, p, q] = zipWith VName (map nameFromString ["a", "b", "n", "m", "k", "l", "p", "q"]) [0 ..]++ t_a u = TypeVar u (qualName a) []+ array_a u s = Array u s $ t_a mempty+ tp_a = TypeParamType Unlifted a mempty++ t_b u = TypeVar u (qualName b) []+ array_b u s = Array u s $ t_b mempty+ tp_b = TypeParamType Unlifted b mempty++ [sp_n, sp_m, sp_k, sp_l, sp_p, sp_q] = map (`TypeParamDim` mempty) [n, m, k, l, p, q]++ size = flip sizeFromName mempty . qualName+ shape = Shape . map size++ tuple_array u x y s =+ Array u s (Record (M.fromList $ zip tupleFieldNames [x, y]))++ arr x y = Scalar $ Arrow mempty Unnamed Observe x (RetType [] y)+ carr x y = Scalar $ Arrow mempty Unnamed Consume x (RetType [] y)++ array_ka u = Array u (Shape [sizeFromName (qualName k) mempty]) $ t_a mempty++ accType u t =+ TypeVar u (qualName (fst intrinsicAcc)) [TypeArgType t]++ namify i (x, y) = (VName (nameFromString x) i, y)++ primFun (name, (ts, t, _)) =+ (name, IntrinsicMonoFun (map unPrim ts) $ unPrim t)++ unOpFun bop = (prettyString bop, IntrinsicMonoFun [t] t)+ where+ t = unPrim $ Primitive.unOpType bop++ binOpFun bop = (prettyString bop, IntrinsicMonoFun [t, t] t)+ where+ t = unPrim $ Primitive.binOpType bop++ cmpOpFun bop = (prettyString bop, IntrinsicMonoFun [t, t] Bool)+ where+ t = unPrim $ Primitive.cmpOpType bop++ convOpFun cop = (prettyString cop, IntrinsicMonoFun [unPrim ft] $ unPrim tt)+ where+ (ft, tt) = Primitive.convOpType cop++ signFun t = ("sign_" ++ prettyString t, IntrinsicMonoFun [Unsigned t] $ Signed t)++ unsignFun t = ("unsign_" ++ prettyString t, IntrinsicMonoFun [Signed t] $ Unsigned t)++ unPrim (Primitive.IntType t) = Signed t+ unPrim (Primitive.FloatType t) = FloatType t+ unPrim Primitive.Bool = Bool+ unPrim Primitive.Unit = Bool++ intrinsicPrim t = (prettyString t, IntrinsicType Unlifted [] $ Scalar $ Prim t)++ anyIntType =+ map Signed [minBound .. maxBound]+ ++ map Unsigned [minBound .. maxBound]+ anyNumberType =+ anyIntType+ ++ map FloatType [minBound .. maxBound]+ anyPrimType = Bool : anyNumberType++ mkIntrinsicBinOp :: BinOp -> Maybe (String, Intrinsic)+ mkIntrinsicBinOp op = do+ op' <- intrinsicBinOp op+ pure (prettyString op, op')++ binOp ts = Just $ IntrinsicOverloadedFun ts [Nothing, Nothing] Nothing+ ordering = Just $ IntrinsicOverloadedFun anyPrimType [Nothing, Nothing] (Just Bool)++ intrinsicBinOp Plus = binOp anyNumberType+ intrinsicBinOp Minus = binOp anyNumberType+ intrinsicBinOp Pow = binOp anyNumberType+ intrinsicBinOp Times = binOp anyNumberType+ intrinsicBinOp Divide = binOp anyNumberType+ intrinsicBinOp Mod = binOp anyNumberType+ intrinsicBinOp Quot = binOp anyIntType+ intrinsicBinOp Rem = binOp anyIntType+ intrinsicBinOp ShiftR = binOp anyIntType+ intrinsicBinOp ShiftL = binOp anyIntType+ intrinsicBinOp Band = binOp anyIntType+ intrinsicBinOp Xor = binOp anyIntType+ intrinsicBinOp Bor = binOp anyIntType+ intrinsicBinOp LogAnd = binOp [Bool]+ intrinsicBinOp LogOr = binOp [Bool]+ intrinsicBinOp Equal = Just IntrinsicEquality+ intrinsicBinOp NotEqual = Just IntrinsicEquality+ intrinsicBinOp Less = ordering+ intrinsicBinOp Leq = ordering+ intrinsicBinOp Greater = ordering+ intrinsicBinOp Geq = ordering+ intrinsicBinOp _ = Nothing++ tupInt64 1 =+ Prim $ Signed Int64+ tupInt64 x =+ tupleRecord $ replicate x $ Scalar $ Prim $ Signed Int64++-- | Is this include part of the built-in prelude?+isBuiltin :: FilePath -> Bool+isBuiltin = (== "/prelude") . takeDirectory++-- | Is the position of this thing builtin as per 'isBuiltin'? Things+-- without location are considered not built-in.+isBuiltinLoc :: Located a => a -> Bool+isBuiltinLoc x =+ case locOf x of+ NoLoc -> False+ Loc pos _ -> isBuiltin $ posFile pos++-- | The largest tag used by an intrinsic - this can be used to+-- determine whether a 'VName' refers to an intrinsic or a user-defined name.+maxIntrinsicTag :: Int+maxIntrinsicTag = maxinum $ map baseTag $ M.keys intrinsics++-- | Create a name with no qualifiers from a name.+qualName :: v -> QualName v+qualName = QualName []++-- | Add another qualifier (at the head) to a qualified name.+qualify :: v -> QualName v -> QualName v+qualify k (QualName ks v) = QualName (k : ks) v++-- | The modules imported by a Futhark program.+progImports :: ProgBase f vn -> [(String, Loc)]+progImports = concatMap decImports . progDecs++-- | The modules imported by a single declaration.+decImports :: DecBase f vn -> [(String, Loc)]+decImports (OpenDec x _) = modExpImports x+decImports (ModDec md) = modExpImports $ modExp md+decImports SigDec {} = []+decImports TypeDec {} = []+decImports ValDec {} = []+decImports (LocalDec d _) = decImports d+decImports (ImportDec x _ loc) = [(x, locOf loc)]++modExpImports :: ModExpBase f vn -> [(String, Loc)]+modExpImports ModVar {} = []+modExpImports (ModParens p _) = modExpImports p+modExpImports (ModImport f _ loc) = [(f, locOf loc)]+modExpImports (ModDecs ds _) = concatMap decImports ds+modExpImports (ModApply _ me _ _ _) = modExpImports me+modExpImports (ModAscript me _ _ _) = modExpImports me+modExpImports ModLambda {} = []++-- | The set of module types used in any exported (non-local)+-- declaration.+progModuleTypes :: ProgBase Info VName -> S.Set VName+progModuleTypes prog = foldMap reach mtypes_used+ where+ -- Fixed point iteration.+ reach v = S.singleton v <> maybe mempty (foldMap reach) (M.lookup v reachable_from_mtype)++ reachable_from_mtype = foldMap onDec $ progDecs prog+ where+ onDec OpenDec {} = mempty+ onDec ModDec {} = mempty+ onDec (SigDec sb) =+ M.singleton (sigName sb) (onSigExp (sigExp sb))+ onDec TypeDec {} = mempty+ onDec ValDec {} = mempty+ onDec (LocalDec d _) = onDec d+ onDec ImportDec {} = mempty++ onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v+ onSigExp (SigParens e _) = onSigExp e+ onSigExp (SigSpecs ss _) = foldMap onSpec ss+ onSigExp (SigWith e _ _) = onSigExp e+ onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2++ onSpec ValSpec {} = mempty+ onSpec TypeSpec {} = mempty+ onSpec TypeAbbrSpec {} = mempty+ onSpec (ModSpec vn e _ _) = S.singleton vn <> onSigExp e+ onSpec (IncludeSpec e _) = onSigExp e++ mtypes_used = foldMap onDec $ progDecs prog+ where+ onDec (OpenDec x _) = onModExp x+ onDec (ModDec md) =+ maybe mempty (onSigExp . fst) (modSignature md) <> onModExp (modExp md)+ onDec SigDec {} = mempty+ onDec TypeDec {} = mempty+ onDec ValDec {} = mempty+ onDec LocalDec {} = mempty+ onDec ImportDec {} = mempty++ onModExp ModVar {} = mempty+ onModExp (ModParens p _) = onModExp p+ onModExp ModImport {} = mempty+ onModExp (ModDecs ds _) = mconcat $ map onDec ds+ onModExp (ModApply me1 me2 _ _ _) = onModExp me1 <> onModExp me2+ onModExp (ModAscript me se _ _) = onModExp me <> onSigExp se+ onModExp (ModLambda p r me _) =+ onModParam p <> maybe mempty (onSigExp . fst) r <> onModExp me++ onModParam = onSigExp . modParamType++ onSigExp (SigVar v _ _) = S.singleton $ qualLeaf v+ onSigExp (SigParens e _) = onSigExp e+ onSigExp SigSpecs {} = mempty+ onSigExp (SigWith e _ _) = onSigExp e+ onSigExp (SigArrow _ e1 e2 _) = onSigExp e1 <> onSigExp e2++-- | Extract a leading @((name, namespace, file), remainder)@ from a+-- documentation comment string. These are formatted as+-- \`name\`\@namespace[\@file]. Let us hope that this pattern does not occur+-- anywhere else.+identifierReference :: String -> Maybe ((String, String, Maybe FilePath), String)+identifierReference ('`' : s)+ | (identifier, '`' : '@' : s') <- break (== '`') s,+ (namespace, s'') <- span isAlpha s',+ not $ null namespace =+ case s'' of+ '@' : '"' : s'''+ | (file, '"' : s'''') <- span (/= '"') s''' ->+ Just ((identifier, namespace, Just file), s'''')+ _ -> Just ((identifier, namespace, Nothing), s'')+identifierReference _ = Nothing++-- | Given an operator name, return the operator that determines its+-- syntactical properties.+leadingOperator :: Name -> BinOp+leadingOperator s =+ maybe Backtick snd $+ find ((`isPrefixOf` s') . fst) $+ sortOn (Down . length . fst) $+ zip (map prettyString operators) operators+ where+ s' = nameToString s+ operators :: [BinOp]+ operators = [minBound .. maxBound :: BinOp]++-- | Find instances of typed holes in the program.+progHoles :: ProgBase Info VName -> [(Loc, StructType)]+progHoles = foldMap holesInDec . progDecs+ where+ holesInDec (ValDec vb) = holesInExp $ valBindBody vb+ holesInDec (ModDec me) = holesInModExp $ modExp me+ holesInDec (OpenDec me _) = holesInModExp me+ holesInDec (LocalDec d _) = holesInDec d+ holesInDec TypeDec {} = mempty+ holesInDec SigDec {} = mempty+ holesInDec ImportDec {} = mempty++ holesInModExp (ModDecs ds _) = foldMap holesInDec ds+ holesInModExp (ModParens me _) = holesInModExp me+ holesInModExp (ModApply x y _ _ _) = holesInModExp x <> holesInModExp y+ holesInModExp (ModAscript me _ _ _) = holesInModExp me+ holesInModExp (ModLambda _ _ me _) = holesInModExp me+ holesInModExp ModVar {} = mempty+ holesInModExp ModImport {} = mempty++ holesInExp = flip execState mempty . onExp++ onExp e@(Hole (Info t) loc) = do+ modify ((locOf loc, toStruct t) :)+ pure e+ onExp e = astMap (identityMapper {mapOnExp = onExp}) e++-- | Strip semantically irrelevant stuff from the top level of+-- expression. This is used to provide a slightly fuzzy notion of+-- expression equality.+--+-- Ideally we'd implement unification on a simpler representation that+-- simply didn't allow us.+stripExp :: Exp -> Maybe Exp+stripExp (Parens e _) = stripExp e `mplus` Just e+stripExp (Assert _ e _ _) = stripExp e `mplus` Just e+stripExp (Attr _ e _) = stripExp e `mplus` Just e+stripExp (Ascript e _ _) = stripExp e `mplus` Just e+stripExp _ = Nothing++similarSlices :: Slice -> Slice -> Maybe [(Exp, Exp)]+similarSlices slice1 slice2+ | length slice1 == length slice2 = do+ concat <$> zipWithM match slice1 slice2+ | otherwise = Nothing+ where+ match (DimFix e1) (DimFix e2) = Just [(e1, e2)]+ match (DimSlice a1 b1 c1) (DimSlice a2 b2 c2) =+ concat <$> sequence [pair (a1, a2), pair (b1, b2), pair (c1, c2)]+ match _ _ = Nothing+ pair (Nothing, Nothing) = Just []+ pair (Just x, Just y) = Just [(x, y)]+ pair _ = Nothing++-- | If these two expressions are structurally similar at top level as+-- sizes, produce their subexpressions (which are not necessarily+-- similar, but you can check for that!). This is the machinery+-- underlying expresssion unification.+similarExps :: Exp -> Exp -> Maybe [(Exp, Exp)]+similarExps e1 e2 | bareExp e1 == bareExp e2 = Just []+similarExps e1 e2 | Just e1' <- stripExp e1 = similarExps e1' e2+similarExps e1 e2 | Just e2' <- stripExp e2 = similarExps e1 e2'+similarExps+ (AppExp (BinOp (op1, _) _ (x1, _) (y1, _) _) _)+ (AppExp (BinOp (op2, _) _ (x2, _) (y2, _) _) _)+ | op1 == op2 = Just [(x1, x2), (y1, y2)]+similarExps (AppExp (Apply f1 args1 _) _) (AppExp (Apply f2 args2 _) _)+ | f1 == f2 = Just $ zip (map snd $ NE.toList args1) (map snd $ NE.toList args2)+similarExps (AppExp (Index arr1 slice1 _) _) (AppExp (Index arr2 slice2 _) _)+ | arr1 == arr2,+ length slice1 == length slice2 =+ similarSlices slice1 slice2+similarExps (TupLit es1 _) (TupLit es2 _)+ | length es1 == length es2 =+ Just $ zip es1 es2+similarExps (RecordLit fs1 _) (RecordLit fs2 _)+ | length fs1 == length fs2 =+ zipWithM onFields fs1 fs2+ where+ onFields (RecordFieldExplicit n1 fe1 _) (RecordFieldExplicit n2 fe2 _)+ | n1 == n2 = Just (fe1, fe2)+ onFields (RecordFieldImplicit vn1 ty1 _) (RecordFieldImplicit vn2 ty2 _) =+ Just (Var (qualName vn1) ty1 mempty, Var (qualName vn2) ty2 mempty)+ onFields _ _ = Nothing+similarExps (ArrayLit es1 _ _) (ArrayLit es2 _ _)+ | length es1 == length es2 =+ Just $ zip es1 es2+similarExps (Project field1 e1 _ _) (Project field2 e2 _ _)+ | field1 == field2 =+ Just [(e1, e2)]+similarExps (Negate e1 _) (Negate e2 _) =+ Just [(e1, e2)]+similarExps (Not e1 _) (Not e2 _) =+ Just [(e1, e2)]+similarExps (Constr n1 es1 _ _) (Constr n2 es2 _ _)+ | length es1 == length es2,+ n1 == n2 =+ Just $ zip es1 es2+similarExps (Update e1 slice1 e'1 _) (Update e2 slice2 e'2 _) =+ ([(e1, e2), (e'1, e'2)] ++) <$> similarSlices slice1 slice2+similarExps (RecordUpdate e1 names1 e'1 _ _) (RecordUpdate e2 names2 e'2 _ _)+ | names1 == names2 =+ Just [(e1, e2), (e'1, e'2)]+similarExps (OpSection op1 _ _) (OpSection op2 _ _)+ | op1 == op2 = Just []+similarExps (OpSectionLeft op1 _ x1 _ _ _) (OpSectionLeft op2 _ x2 _ _ _)+ | op1 == op2 = Just [(x1, x2)]+similarExps (OpSectionRight op1 _ x1 _ _ _) (OpSectionRight op2 _ x2 _ _ _)+ | op1 == op2 = Just [(x1, x2)]+similarExps (ProjectSection names1 _ _) (ProjectSection names2 _ _)+ | names1 == names2 = Just []+similarExps (IndexSection slice1 _ _) (IndexSection slice2 _ _) =+ similarSlices slice1 slice2+similarExps _ _ = Nothing++-- | An identifier with type- and aliasing information.+type Ident = IdentBase Info VName++-- | An index with type information.+type DimIndex = DimIndexBase Info VName++-- | A slice with type information.+type Slice = SliceBase Info VName++-- | An expression with type information.+type Exp = ExpBase Info VName++-- | An application expression with type information.+type AppExp = AppExpBase Info VName++-- | A pattern with type information.+type Pat = PatBase Info VName++-- | An constant declaration with type information.+type ValBind = ValBindBase Info VName++-- | A type binding with type information.+type TypeBind = TypeBindBase Info VName++-- | A type-checked module binding.+type ModBind = ModBindBase Info VName++-- | A type-checked module type binding.+type SigBind = SigBindBase Info VName++-- | A type-checked module expression.+type ModExp = ModExpBase Info VName++-- | A type-checked module parameter.+type ModParam = ModParamBase Info VName++-- | A type-checked module type expression.+type SigExp = SigExpBase Info VName++-- | A type-checked declaration.+type Dec = DecBase Info VName++-- | A type-checked specification.+type Spec = SpecBase Info VName++-- | An Futhark program with type information.+type Prog = ProgBase Info VName++-- | A known type arg with shape annotations.+type StructTypeArg = TypeArg Size++-- | A type-checked type parameter.+type TypeParam = TypeParamBase VName++-- | A known scalar type with no shape annotations.+type ScalarType = ScalarTypeBase ()++-- | A type-checked case (of a match expression).+type Case = CaseBase Info VName++-- | A type with no aliasing information but shape annotations.+type UncheckedType = TypeBase (Shape Name) ()++-- | An unchecked type expression.+type UncheckedTypeExp = TypeExp NoInfo Name++-- | An identifier with no type annotations.+type UncheckedIdent = IdentBase NoInfo Name++-- | An index with no type annotations.+type UncheckedDimIndex = DimIndexBase NoInfo Name++-- | A slice with no type annotations.+type UncheckedSlice = SliceBase NoInfo Name++-- | An expression with no type annotations.+type UncheckedExp = ExpBase NoInfo Name++-- | A module expression with no type annotations.+type UncheckedModExp = ModExpBase NoInfo Name++-- | A module type expression with no type annotations.+type UncheckedSigExp = SigExpBase NoInfo Name++-- | A type parameter with no type annotations.+type UncheckedTypeParam = TypeParamBase Name++-- | A pattern with no type annotations.+type UncheckedPat = PatBase NoInfo Name++-- | A function declaration with no type annotations.+type UncheckedValBind = ValBindBase NoInfo Name++-- | A type binding with no type annotations.+type UncheckedTypeBind = TypeBindBase NoInfo Name++-- | A module type binding with no type annotations.+type UncheckedSigBind = SigBindBase NoInfo Name++-- | A module binding with no type annotations.+type UncheckedModBind = ModBindBase NoInfo Name -- | A declaration with no type annotations. type UncheckedDec = DecBase NoInfo Name
src/Language/Futhark/Query.hs view
@@ -46,7 +46,7 @@ sizeDefs (SizeBinder v loc) = M.singleton v $ DefBound $ BoundTerm (Scalar (Prim (Signed Int64))) (locOf loc) -patternDefs :: Pat -> Defs+patternDefs :: Pat (TypeBase Size u) -> Defs patternDefs (Id vn (Info t) loc) = M.singleton vn $ DefBound $ BoundTerm (toStruct t) (locOf loc) patternDefs (TuplePat pats _) =@@ -88,7 +88,7 @@ Lambda params _ _ _ _ -> mconcat (map patternDefs params) AppExp (LetFun name (tparams, params, _, Info ret, _) _ loc) _ ->- let name_t = foldFunTypeFromParams params ret+ let name_t = funType params ret in M.singleton name (DefBound $ BoundTerm name_t (locOf loc)) <> mconcat (map typeParamDefs tparams) <> mconcat (map patternDefs params)@@ -111,9 +111,7 @@ <> expDefs (valBindBody vbind) where vbind_t =- foldFunTypeFromParams (valBindParams vbind) $- unInfo $- valBindRetType vbind+ funType (valBindParams vbind) $ unInfo $ valBindRetType vbind typeBindDefs :: TypeBind -> Defs typeBindDefs tbind =@@ -236,7 +234,7 @@ inDim (SizeExp e _) = atPosInExp e pos inDim SizeExpAny {} = Nothing -atPosInPat :: Pat -> Pos -> Maybe RawAtPos+atPosInPat :: Pat (TypeBase Size u) -> Pos -> Maybe RawAtPos atPosInPat (Id vn _ loc) pos = do guard $ loc `contains` pos Just $ RawAtName (qualName vn) $ locOf loc@@ -274,7 +272,7 @@ | merge `contains` pos = atPosInPat merge pos atPosInExp (Ascript _ te _) pos | te `contains` pos = atPosInTypeExp te pos-atPosInExp (AppExp (Coerce _ te _) _) pos+atPosInExp (Coerce _ te _ _) pos | te `contains` pos = atPosInTypeExp te pos atPosInExp e pos = do guard $ e `contains` pos
src/Language/Futhark/Syntax.hs view
@@ -19,7 +19,7 @@ IntType (..), FloatType (..), PrimType (..),- Size (..),+ Size, Shape (..), shapeRank, stripDims,@@ -31,10 +31,11 @@ PName (..), ScalarTypeBase (..), RetTypeBase (..),- PatType, StructType,+ ParamType,+ ResType, StructRetType,- PatRetType,+ ResRetType, ValueType, Diet (..), @@ -88,11 +89,11 @@ -- * Miscellaneous NoInfo (..), Info (..),- Alias (..),- Aliasing, QualName (..), mkApply, mkApplyUT,+ sizeFromName,+ sizeFromInteger, ) where @@ -106,7 +107,6 @@ import Data.Map.Strict qualified as M import Data.Monoid hiding (Sum) import Data.Ord-import Data.Set qualified as S import Data.Text qualified as T import Data.Traversable import Futhark.Util.Loc@@ -217,21 +217,17 @@ | AttrComp Name [AttrInfo vn] SrcLoc deriving (Eq, Ord, Show) --- | The elaborated size of a dimension.-data Size- = -- | The size of the dimension is this name, which- -- must be in scope. In a return type, this will- -- give rise to an assertion.- NamedSize (QualName VName)- | -- | The size is a constant.- ConstSize Int64- | -- | No known size. If @Nothing@, then this is a name distinct- -- from any other. The type checker should _never_ produce these- -- - they are a (hopefully temporary) thing introduced by- -- defunctorisation and monomorphisation.- AnySize (Maybe VName)- deriving (Eq, Ord, Show)+-- | The elaborated size of a dimension is just an expression.+type Size = ExpBase Info VName +-- | Create a 'Size' from a name.+sizeFromName :: QualName VName -> SrcLoc -> Size+sizeFromName name = Var name (Info $ Scalar $ Prim $ Signed Int64)++-- | Create a 'Size' from a constant integer.+sizeFromInteger :: Integer -> SrcLoc -> Size+sizeFromInteger x = IntLit x (Info <$> Scalar $ Prim $ Signed Int64)+ -- | The size of an array type is a list of its dimension sizes. If -- 'Nothing', that dimension is of a (statically) unknown size. newtype Shape dim = Shape {shapeDims :: [dim]}@@ -287,8 +283,14 @@ bitraverse f g (RetType dims t) = RetType dims <$> bitraverse f g t instance Functor (RetTypeBase dim) where- fmap = second+ fmap = fmapDefault +instance Foldable (RetTypeBase dim) where+ foldMap = foldMapDefault++instance Traversable (RetTypeBase dim) where+ traverse = bitraverse pure+ instance Bifunctor RetTypeBase where bimap = bimapDefault @@ -298,28 +300,34 @@ -- | Types that can be elements of arrays. This representation does -- allow arrays of records of functions, which is nonsensical, but it -- convolutes the code too much if we try to statically rule it out.-data ScalarTypeBase dim as+data ScalarTypeBase dim u = Prim PrimType- | TypeVar as Uniqueness (QualName VName) [TypeArg dim]- | Record (M.Map Name (TypeBase dim as))- | Sum (M.Map Name [TypeBase dim as])+ | TypeVar u (QualName VName) [TypeArg dim]+ | Record (M.Map Name (TypeBase dim u))+ | Sum (M.Map Name [TypeBase dim u]) | -- | The aliasing corresponds to the lexical -- closure of the function.- Arrow as PName Diet (TypeBase dim ()) (RetTypeBase dim as)+ Arrow u PName Diet (TypeBase dim NoUniqueness) (RetTypeBase dim Uniqueness) deriving (Eq, Ord, Show) instance Bitraversable ScalarTypeBase where bitraverse _ _ (Prim t) = pure $ Prim t bitraverse f g (Record fs) = Record <$> traverse (bitraverse f g) fs- bitraverse f g (TypeVar als u t args) =- TypeVar <$> g als <*> pure u <*> pure t <*> traverse (traverse f) args- bitraverse f g (Arrow als v d t1 t2) =- Arrow <$> g als <*> pure v <*> pure d <*> bitraverse f pure t1 <*> bitraverse f g t2+ bitraverse f g (TypeVar als t args) =+ TypeVar <$> g als <*> pure t <*> traverse (traverse f) args+ bitraverse f g (Arrow u v d t1 t2) =+ Arrow <$> g u <*> pure v <*> pure d <*> bitraverse f pure t1 <*> bitraverse f pure t2 bitraverse f g (Sum cs) = Sum <$> (traverse . traverse) (bitraverse f g) cs instance Functor (ScalarTypeBase dim) where- fmap = second+ fmap = fmapDefault +instance Foldable (ScalarTypeBase dim) where+ foldMap = foldMapDefault++instance Traversable (ScalarTypeBase dim) where+ traverse = bitraverse pure+ instance Bifunctor ScalarTypeBase where bimap = bimapDefault @@ -331,19 +339,25 @@ -- function parameter names are ignored. This representation permits -- some malformed types (arrays of functions), but importantly rules -- out arrays-of-arrays.-data TypeBase dim as- = Scalar (ScalarTypeBase dim as)- | Array as Uniqueness (Shape dim) (ScalarTypeBase dim ())+data TypeBase dim u+ = Scalar (ScalarTypeBase dim u)+ | Array u (Shape dim) (ScalarTypeBase dim NoUniqueness) deriving (Eq, Ord, Show) instance Bitraversable TypeBase where bitraverse f g (Scalar t) = Scalar <$> bitraverse f g t- bitraverse f g (Array a u shape t) =- Array <$> g a <*> pure u <*> traverse f shape <*> bitraverse f pure t+ bitraverse f g (Array als shape t) =+ Array <$> g als <*> traverse f shape <*> bitraverse f pure t instance Functor (TypeBase dim) where- fmap = second+ fmap = fmapDefault +instance Foldable (TypeBase dim) where+ foldMap = foldMapDefault++instance Traversable (TypeBase dim) where+ traverse = bitraverse pure+ instance Bifunctor TypeBase where bimap = bimapDefault @@ -352,13 +366,13 @@ -- | An argument passed to a type constructor. data TypeArg dim- = TypeArgDim dim SrcLoc- | TypeArgType (TypeBase dim ()) SrcLoc+ = TypeArgDim dim+ | TypeArgType (TypeBase dim NoUniqueness) deriving (Eq, Ord, Show) instance Traversable TypeArg where- traverse f (TypeArgDim v loc) = TypeArgDim <$> f v <*> pure loc- traverse f (TypeArgType t loc) = TypeArgType <$> bitraverse f pure t <*> pure loc+ traverse f (TypeArgDim v) = TypeArgDim <$> f v+ traverse f (TypeArgType t) = TypeArgType <$> bitraverse f pure t instance Functor TypeArg where fmap = fmapDefault@@ -366,35 +380,25 @@ instance Foldable TypeArg where foldMap = foldMapDefault --- | A variable that is aliased. Can be still in-scope, or have gone--- out of scope and be free. In the latter case, it behaves more like--- an equivalence class. See uniqueness-error18.fut for an example of--- why this is necessary.-data Alias- = AliasBound {aliasVar :: VName}- | AliasFree {aliasVar :: VName}- deriving (Eq, Ord, Show)---- | Aliasing for a type, which is a set of the variables that are--- aliased.-type Aliasing = S.Set Alias---- | A type with aliasing information and shape annotations, used for--- describing the type patterns and expressions.-type PatType = TypeBase Size Aliasing- -- | A "structural" type with shape annotations and no aliasing -- information, used for declarations.-type StructType = TypeBase Size ()+type StructType = TypeBase Size NoUniqueness +-- | A type with consumption information, used for function parameters+-- (but not in function types).+type ParamType = TypeBase Size Diet++-- | A type with uniqueness information, used for function return types+type ResType = TypeBase Size Uniqueness+ -- | A value type contains full, manifest size information.-type ValueType = TypeBase Int64 ()+type ValueType = TypeBase Int64 NoUniqueness --- | The return type version of 'StructType'.-type StructRetType = RetTypeBase Size ()+-- | The return type version of a 'ResType'.+type StructRetType = RetTypeBase Size NoUniqueness --- | The return type version of 'PatType'.-type PatRetType = RetTypeBase Size Aliasing+-- | The return type version of a 'StructType'.+type ResRetType = RetTypeBase Size Uniqueness -- | A dimension declaration expression for use in a 'TypeExp'. -- Syntactically includes the brackets.@@ -491,25 +495,31 @@ Consume deriving (Eq, Ord, Show) +instance Semigroup Diet where+ (<>) = max++instance Monoid Diet where+ mempty = Observe+ -- | An identifier consists of its name and the type of the value -- bound to the identifier.-data IdentBase f vn = Ident+data IdentBase f vn t = Ident { identName :: vn,- identType :: f PatType,+ identType :: f t, identSrcLoc :: SrcLoc } -deriving instance Show (IdentBase Info VName)+deriving instance Show (Info t) => Show (IdentBase Info VName t) -deriving instance Show vn => Show (IdentBase NoInfo vn)+deriving instance (Show (Info t), Show vn) => Show (IdentBase NoInfo vn t) -instance Eq vn => Eq (IdentBase ty vn) where+instance Eq vn => Eq (IdentBase ty vn t) where x == y = identName x == identName y -instance Ord vn => Ord (IdentBase ty vn) where+instance Ord vn => Ord (IdentBase ty vn t) where compare = comparing identName -instance Located (IdentBase ty vn) where+instance Located (IdentBase ty vn t) where locOf = locOf . identSrcLoc -- | Default binary operators.@@ -654,15 +664,13 @@ -- constructing 'Apply' directly. -- -- The @Maybe VNames@ are existential sizes generated by this- -- argumnet. May have duplicates across the program, but they+ -- argument. May have duplicates across the program, but they -- will all produce the same value (the expressions will be -- identical). Apply (ExpBase f vn) (NE.NonEmpty (f (Diet, Maybe VName), ExpBase f vn)) SrcLoc- | -- | Size coercion: @e :> t@.- Coerce (ExpBase f vn) (TypeExp f vn) SrcLoc | Range (ExpBase f vn) (Maybe (ExpBase f vn))@@ -670,16 +678,16 @@ SrcLoc | LetPat [SizeBinder vn]- (PatBase f vn)+ (PatBase f vn StructType) (ExpBase f vn) (ExpBase f vn) SrcLoc | LetFun vn ( [TypeParamBase vn],- [PatBase f vn],+ [PatBase f vn ParamType], Maybe (TypeExp f vn),- f StructRetType,+ f ResRetType, ExpBase f vn ) (ExpBase f vn)@@ -687,20 +695,20 @@ | If (ExpBase f vn) (ExpBase f vn) (ExpBase f vn) SrcLoc | DoLoop [VName] -- Size parameters.- (PatBase f vn) -- Merge variable pattern.+ (PatBase f vn ParamType) -- Merge variable pattern. (ExpBase f vn) -- Initial values of merge variables. (LoopFormBase f vn) -- Do or while loop. (ExpBase f vn) -- Loop body. SrcLoc | BinOp (QualName vn, SrcLoc)- (f PatType)- (ExpBase f vn, f (StructType, Maybe VName))- (ExpBase f vn, f (StructType, Maybe VName))+ (f StructType)+ (ExpBase f vn, f (Maybe VName))+ (ExpBase f vn, f (Maybe VName)) SrcLoc | LetWith- (IdentBase f vn)- (IdentBase f vn)+ (IdentBase f vn StructType)+ (IdentBase f vn StructType) (SliceBase f vn) (ExpBase f vn) (ExpBase f vn)@@ -725,7 +733,6 @@ locOf (Range _ _ _ pos) = locOf pos locOf (BinOp _ _ _ _ loc) = locOf loc locOf (If _ _ _ loc) = locOf loc- locOf (Coerce _ _ loc) = locOf loc locOf (Apply _ _ loc) = locOf loc locOf (LetPat _ _ _ _ loc) = locOf loc locOf (LetFun _ _ _ loc) = locOf loc@@ -739,7 +746,7 @@ -- annotation encodes the result type, as well as any existential -- sizes that are generated here. data AppRes = AppRes- { appResType :: PatType,+ { appResType :: StructType, appResExt :: [VName] } deriving (Eq, Ord, Show)@@ -754,14 +761,14 @@ data ExpBase f vn = Literal PrimValue SrcLoc | -- | A polymorphic integral literal.- IntLit Integer (f PatType) SrcLoc+ IntLit Integer (f StructType) SrcLoc | -- | A polymorphic decimal literal.- FloatLit Double (f PatType) SrcLoc+ FloatLit Double (f StructType) SrcLoc | -- | A string literal is just a fancy syntax for an array -- of bytes. StringLit [Word8] SrcLoc- | Hole (f PatType) SrcLoc- | Var (QualName vn) (f PatType) SrcLoc+ | Hole (f StructType) SrcLoc+ | Var (QualName vn) (f StructType) SrcLoc | -- | A parenthesized expression. Parens (ExpBase f vn) SrcLoc | QualParens (QualName vn, SrcLoc) (ExpBase f vn) SrcLoc@@ -771,10 +778,10 @@ RecordLit [FieldBase f vn] SrcLoc | -- | Array literals, e.g., @[ [1+x, 3], [2, 1+4] ]@. -- Second arg is the row type of the rows of the array.- ArrayLit [ExpBase f vn] (f PatType) SrcLoc+ ArrayLit [ExpBase f vn] (f StructType) SrcLoc | -- | An attribute applied to the following expression. Attr (AttrInfo vn) (ExpBase f vn) SrcLoc- | Project Name (ExpBase f vn) (f PatType) SrcLoc+ | Project Name (ExpBase f vn) (f StructType) SrcLoc | -- | Numeric negation (ugly special case; Haskell did it first). Negate (ExpBase f vn) SrcLoc | -- | Logical and bitwise negation.@@ -784,39 +791,41 @@ -- does. Assert (ExpBase f vn) (ExpBase f vn) (f T.Text) SrcLoc | -- | An n-ary value constructor.- Constr Name [ExpBase f vn] (f PatType) SrcLoc+ Constr Name [ExpBase f vn] (f StructType) SrcLoc | Update (ExpBase f vn) (SliceBase f vn) (ExpBase f vn) SrcLoc- | RecordUpdate (ExpBase f vn) [Name] (ExpBase f vn) (f PatType) SrcLoc+ | RecordUpdate (ExpBase f vn) [Name] (ExpBase f vn) (f StructType) SrcLoc | Lambda- [PatBase f vn]+ [PatBase f vn ParamType] (ExpBase f vn) (Maybe (TypeExp f vn))- (f (Aliasing, StructRetType))+ (f ResRetType) SrcLoc | -- | @+@; first two types are operands, third is result.- OpSection (QualName vn) (f PatType) SrcLoc+ OpSection (QualName vn) (f StructType) SrcLoc | -- | @2+@; first type is operand, second is result. OpSectionLeft (QualName vn)- (f PatType)+ (f StructType) (ExpBase f vn)- (f (PName, StructType, Maybe VName), f (PName, StructType))- (f PatRetType, f [VName])+ (f (PName, ParamType, Maybe VName), f (PName, ParamType))+ (f ResRetType, f [VName]) SrcLoc | -- | @+2@; first type is operand, second is result. OpSectionRight (QualName vn)- (f PatType)+ (f StructType) (ExpBase f vn)- (f (PName, StructType), f (PName, StructType, Maybe VName))- (f PatRetType)+ (f (PName, ParamType), f (PName, ParamType, Maybe VName))+ (f ResRetType) SrcLoc | -- | Field projection as a section: @(.x.y.z)@.- ProjectSection [Name] (f PatType) SrcLoc+ ProjectSection [Name] (f StructType) SrcLoc | -- | Array indexing as a section: @(.[i,j])@.- IndexSection (SliceBase f vn) (f PatType) SrcLoc+ IndexSection (SliceBase f vn) (f StructType) SrcLoc | -- | Type ascription: @e : t@. Ascript (ExpBase f vn) (TypeExp f vn) SrcLoc+ | -- | Size coercion: @e :> t@.+ Coerce (ExpBase f vn) (TypeExp f vn) (f StructType) SrcLoc | AppExp (AppExpBase f vn) (f AppRes) deriving instance Show (ExpBase Info VName)@@ -844,6 +853,7 @@ locOf (StringLit _ loc) = locOf loc locOf (Var _ _ loc) = locOf loc locOf (Ascript _ _ loc) = locOf loc+ locOf (Coerce _ _ _ loc) = locOf loc locOf (Negate _ pos) = locOf pos locOf (Not _ pos) = locOf pos locOf (Update _ _ _ pos) = locOf pos@@ -863,7 +873,7 @@ -- | An entry in a record literal. data FieldBase f vn = RecordFieldExplicit Name (ExpBase f vn) SrcLoc- | RecordFieldImplicit vn (f PatType) SrcLoc+ | RecordFieldImplicit vn (f StructType) SrcLoc deriving instance Show (FieldBase Info VName) @@ -882,7 +892,7 @@ locOf (RecordFieldImplicit _ _ loc) = locOf loc -- | A case in a match expression.-data CaseBase f vn = CasePat (PatBase f vn) (ExpBase f vn) SrcLoc+data CaseBase f vn = CasePat (PatBase f vn StructType) (ExpBase f vn) SrcLoc deriving instance Show (CaseBase Info VName) @@ -901,8 +911,8 @@ -- | Whether the loop is a @for@-loop or a @while@-loop. data LoopFormBase f vn- = For (IdentBase f vn) (ExpBase f vn)- | ForIn (PatBase f vn) (ExpBase f vn)+ = For (IdentBase f vn StructType) (ExpBase f vn)+ | ForIn (PatBase f vn StructType) (ExpBase f vn) | While (ExpBase f vn) deriving instance Show (LoopFormBase Info VName)@@ -926,30 +936,30 @@ -- | A pattern as used most places where variables are bound (function -- parameters, @let@ expressions, etc).-data PatBase f vn- = TuplePat [PatBase f vn] SrcLoc- | RecordPat [(Name, PatBase f vn)] SrcLoc- | PatParens (PatBase f vn) SrcLoc- | Id vn (f PatType) SrcLoc- | Wildcard (f PatType) SrcLoc -- Nothing, i.e. underscore.- | PatAscription (PatBase f vn) (TypeExp f vn) SrcLoc- | PatLit PatLit (f PatType) SrcLoc- | PatConstr Name (f PatType) [PatBase f vn] SrcLoc- | PatAttr (AttrInfo vn) (PatBase f vn) SrcLoc+data PatBase f vn t+ = TuplePat [PatBase f vn t] SrcLoc+ | RecordPat [(Name, PatBase f vn t)] SrcLoc+ | PatParens (PatBase f vn t) SrcLoc+ | Id vn (f t) SrcLoc+ | Wildcard (f t) SrcLoc -- Nothing, i.e. underscore.+ | PatAscription (PatBase f vn t) (TypeExp f vn) SrcLoc+ | PatLit PatLit (f t) SrcLoc+ | PatConstr Name (f t) [PatBase f vn t] SrcLoc+ | PatAttr (AttrInfo vn) (PatBase f vn t) SrcLoc -deriving instance Show (PatBase Info VName)+deriving instance Show (Info t) => Show (PatBase Info VName t) -deriving instance Show vn => Show (PatBase NoInfo vn)+deriving instance (Show (NoInfo t), Show vn) => Show (PatBase NoInfo vn t) -deriving instance Eq (PatBase NoInfo VName)+deriving instance Eq (NoInfo t) => Eq (PatBase NoInfo VName t) -deriving instance Eq (PatBase Info VName)+deriving instance Eq (Info t) => Eq (PatBase Info VName t) -deriving instance Ord (PatBase NoInfo VName)+deriving instance Ord (NoInfo t) => Ord (PatBase NoInfo VName t) -deriving instance Ord (PatBase Info VName)+deriving instance Ord (Info t) => Ord (PatBase Info VName t) -instance Located (PatBase f vn) where+instance Located (PatBase f vn t) where locOf (TuplePat _ loc) = locOf loc locOf (RecordPat _ loc) = locOf loc locOf (PatParens _ loc) = locOf loc@@ -960,15 +970,37 @@ locOf (PatConstr _ _ _ loc) = locOf loc locOf (PatAttr _ _ loc) = locOf loc --- | Documentation strings, including source location.-data DocComment = DocComment String SrcLoc+instance Traversable f => Functor (PatBase f vn) where+ fmap = fmapDefault++instance Traversable f => Foldable (PatBase f vn) where+ foldMap = foldMapDefault++instance (Traversable f) => Traversable (PatBase f vn) where+ traverse f (Id v t loc) = Id v <$> traverse f t <*> pure loc+ traverse f (TuplePat ps loc) = TuplePat <$> traverse (traverse f) ps <*> pure loc+ traverse f (RecordPat ps loc) = RecordPat <$> traverse (traverse $ traverse f) ps <*> pure loc+ traverse f (PatParens p loc) = PatParens <$> traverse f p <*> pure loc+ traverse f (Wildcard t loc) = Wildcard <$> traverse f t <*> pure loc+ traverse f (PatAscription p te loc) = PatAscription <$> traverse f p <*> pure te <*> pure loc+ traverse f (PatLit l t loc) = PatLit l <$> traverse f t <*> pure loc+ traverse f (PatConstr c t ps loc) = PatConstr c <$> traverse f t <*> traverse (traverse f) ps <*> pure loc+ traverse f (PatAttr attr p loc) = PatAttr attr <$> traverse f p <*> pure loc++-- | Documentation strings, including source location. The string may+-- contain newline characters, but it does not contain comment prefix+-- markers.+data DocComment = DocComment T.Text SrcLoc deriving (Show) instance Located DocComment where locOf (DocComment _ loc) = locOf loc -- | Part of the type of an entry point. Has an actual type, and--- maybe also an ascribed type expression.+-- maybe also an ascribed type expression. Note that although size+-- expressions in the elaborated type can contain variables, they are+-- no longer in scope, and are considered more like equivalence+-- classes. data EntryType = EntryType { entryType :: StructType, entryAscribed :: Maybe (TypeExp Info VName)@@ -1004,9 +1036,9 @@ valBindRetDecl :: Maybe (TypeExp f vn), -- | If 'valBindParams' is null, then the 'retDims' are brought -- into scope at this point.- valBindRetType :: f StructRetType,+ valBindRetType :: f ResRetType, valBindTypeParams :: [TypeParamBase vn],- valBindParams :: [PatBase f vn],+ valBindParams :: [PatBase f vn ParamType], valBindBody :: ExpBase f vn, valBindDoc :: Maybe DocComment, valBindAttrs :: [AttrInfo vn],
src/Language/Futhark/Traversals.hs view
@@ -26,8 +26,8 @@ ) where +import Data.Bifunctor import Data.List.NonEmpty qualified as NE-import Data.Set qualified as S import Language.Futhark.Syntax -- | Express a monad mapping operation on a syntax node. Each element@@ -37,9 +37,8 @@ { mapOnExp :: ExpBase Info VName -> m (ExpBase Info VName), mapOnName :: VName -> m VName, mapOnStructType :: StructType -> m StructType,- mapOnPatType :: PatType -> m PatType,- mapOnStructRetType :: StructRetType -> m StructRetType,- mapOnPatRetType :: PatRetType -> m PatRetType+ mapOnParamType :: ParamType -> m ParamType,+ mapOnResRetType :: ResRetType -> m ResRetType } -- | An 'ASTMapper' that just leaves its input unchanged.@@ -49,9 +48,8 @@ { mapOnExp = pure, mapOnName = pure, mapOnStructType = pure,- mapOnPatType = pure,- mapOnStructRetType = pure,- mapOnPatRetType = pure+ mapOnParamType = pure,+ mapOnResRetType = pure } -- | The class of things that we can map an 'ASTMapper' across.@@ -95,7 +93,7 @@ <$> mapM (astMap tv) fparams <*> mapM (astMap tv) params <*> traverse (astMap tv) ret- <*> traverse (mapOnStructRetType tv) t+ <*> traverse (mapOnResRetType tv) t <*> mapOnExp tv e ) <*> mapOnExp tv body@@ -108,20 +106,12 @@ <*> mapOnExp tv vexp <*> mapOnExp tv body <*> pure loc- astMap tv (Coerce e tdecl loc) =- Coerce <$> mapOnExp tv e <*> astMap tv tdecl <*> pure loc- astMap tv (BinOp (fname, fname_loc) t (x, Info (xt, xext)) (y, Info (yt, yext)) loc) =+ astMap tv (BinOp (fname, fname_loc) t (x, xext) (y, yext) loc) = BinOp <$> ((,) <$> astMap tv fname <*> pure fname_loc)- <*> traverse (mapOnPatType tv) t- <*> ( (,)- <$> mapOnExp tv x- <*> (Info <$> ((,) <$> mapOnStructType tv xt <*> pure xext))- )- <*> ( (,)- <$> mapOnExp tv y- <*> (Info <$> ((,) <$> mapOnStructType tv yt <*> pure yext))- )+ <*> traverse (mapOnStructType tv) t+ <*> ((,) <$> mapOnExp tv x <*> pure xext)+ <*> ((,) <$> mapOnExp tv y <*> pure yext) <*> pure loc astMap tv (DoLoop sparams mergepat mergeexp form loopbody loc) = DoLoop@@ -138,18 +128,18 @@ astMap tv (Var name t loc) = Var <$> astMap tv name- <*> traverse (mapOnPatType tv) t+ <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Hole t loc) =- Hole <$> traverse (mapOnPatType tv) t <*> pure loc+ Hole <$> traverse (mapOnStructType tv) t <*> pure loc astMap _ (Literal val loc) = pure $ Literal val loc astMap _ (StringLit vs loc) = pure $ StringLit vs loc astMap tv (IntLit val t loc) =- IntLit val <$> traverse (mapOnPatType tv) t <*> pure loc+ IntLit val <$> traverse (mapOnStructType tv) t <*> pure loc astMap tv (FloatLit val t loc) =- FloatLit val <$> traverse (mapOnPatType tv) t <*> pure loc+ FloatLit val <$> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Parens e loc) = Parens <$> mapOnExp tv e <*> pure loc astMap tv (QualParens (name, nameloc) e loc) =@@ -162,9 +152,11 @@ astMap tv (RecordLit fields loc) = RecordLit <$> astMap tv fields <*> pure loc astMap tv (ArrayLit els t loc) =- ArrayLit <$> mapM (mapOnExp tv) els <*> traverse (mapOnPatType tv) t <*> pure loc+ ArrayLit <$> mapM (mapOnExp tv) els <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Ascript e tdecl loc) = Ascript <$> mapOnExp tv e <*> astMap tv tdecl <*> pure loc+ astMap tv (Coerce e tdecl t loc) =+ Coerce <$> mapOnExp tv e <*> astMap tv tdecl <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Negate x loc) = Negate <$> mapOnExp tv x <*> pure loc astMap tv (Not x loc) =@@ -180,10 +172,10 @@ <$> mapOnExp tv src <*> pure fs <*> mapOnExp tv v- <*> (Info <$> mapOnPatType tv t)+ <*> (Info <$> mapOnStructType tv t) <*> pure loc astMap tv (Project field e t loc) =- Project field <$> mapOnExp tv e <*> traverse (mapOnPatType tv) t <*> pure loc+ Project field <$> mapOnExp tv e <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Assert e1 e2 desc loc) = Assert <$> mapOnExp tv e1 <*> mapOnExp tv e2 <*> pure desc <*> pure loc astMap tv (Lambda params body ret t loc) =@@ -191,44 +183,44 @@ <$> mapM (astMap tv) params <*> mapOnExp tv body <*> traverse (astMap tv) ret- <*> traverse (traverse $ mapOnStructRetType tv) t+ <*> traverse (mapOnResRetType tv) t <*> pure loc astMap tv (OpSection name t loc) = OpSection <$> astMap tv name- <*> traverse (mapOnPatType tv) t+ <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (OpSectionLeft name t arg (Info (pa, t1a, argext), Info (pb, t1b)) (ret, retext) loc) = OpSectionLeft <$> astMap tv name- <*> traverse (mapOnPatType tv) t+ <*> traverse (mapOnStructType tv) t <*> mapOnExp tv arg <*> ( (,)- <$> (Info <$> ((pa,,) <$> mapOnStructType tv t1a <*> pure argext))- <*> (Info <$> ((pb,) <$> mapOnStructType tv t1b))+ <$> (Info <$> ((pa,,) <$> mapOnParamType tv t1a <*> pure argext))+ <*> (Info <$> ((pb,) <$> mapOnParamType tv t1b)) )- <*> ((,) <$> traverse (mapOnPatRetType tv) ret <*> traverse (mapM (mapOnName tv)) retext)+ <*> ((,) <$> traverse (mapOnResRetType tv) ret <*> traverse (mapM (mapOnName tv)) retext) <*> pure loc astMap tv (OpSectionRight name t arg (Info (pa, t1a), Info (pb, t1b, argext)) t2 loc) = OpSectionRight <$> astMap tv name- <*> traverse (mapOnPatType tv) t+ <*> traverse (mapOnStructType tv) t <*> mapOnExp tv arg <*> ( (,)- <$> (Info <$> ((pa,) <$> mapOnStructType tv t1a))- <*> (Info <$> ((pb,,) <$> mapOnStructType tv t1b <*> pure argext))+ <$> (Info <$> ((pa,) <$> mapOnParamType tv t1a))+ <*> (Info <$> ((pb,,) <$> mapOnParamType tv t1b <*> pure argext)) )- <*> traverse (mapOnPatRetType tv) t2+ <*> traverse (mapOnResRetType tv) t2 <*> pure loc astMap tv (ProjectSection fields t loc) =- ProjectSection fields <$> traverse (mapOnPatType tv) t <*> pure loc+ ProjectSection fields <$> traverse (mapOnStructType tv) t <*> pure loc astMap tv (IndexSection idxs t loc) = IndexSection <$> mapM (astMap tv) idxs- <*> traverse (mapOnPatType tv) t+ <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Constr name es t loc) =- Constr name <$> traverse (mapOnExp tv) es <*> traverse (mapOnPatType tv) t <*> pure loc+ Constr name <$> traverse (mapOnExp tv) es <*> traverse (mapOnStructType tv) t <*> pure loc astMap tv (Attr attr e loc) = Attr attr <$> mapOnExp tv e <*> pure loc astMap tv (AppExp e res) =@@ -269,11 +261,6 @@ astMap tv (SizeExp e loc) = SizeExp <$> mapOnExp tv e <*> pure loc astMap _ (SizeExpAny loc) = pure $ SizeExpAny loc -instance ASTMappable Size where- astMap tv (NamedSize vn) = NamedSize <$> astMap tv vn- astMap _ (ConstSize k) = pure $ ConstSize k- astMap tv (AnySize vn) = AnySize <$> traverse (mapOnName tv) vn- instance ASTMappable (TypeParamBase VName) where astMap = traverse . mapOnName @@ -285,16 +272,9 @@ <*> maybe (pure Nothing) (fmap Just . mapOnExp tv) j <*> maybe (pure Nothing) (fmap Just . mapOnExp tv) stride -instance ASTMappable Alias where- astMap tv (AliasBound v) = AliasBound <$> mapOnName tv v- astMap tv (AliasFree v) = AliasFree <$> mapOnName tv v--instance ASTMappable Aliasing where- astMap tv = fmap S.fromList . traverse (astMap tv) . S.toList- instance ASTMappable AppRes where astMap tv (AppRes t ext) =- AppRes <$> mapOnPatType tv t <*> pure ext+ AppRes <$> mapOnStructType tv t <*> pure ext type TypeTraverser f t dim1 als1 dim2 als2 = (QualName VName -> f (QualName VName)) ->@@ -308,21 +288,21 @@ TypeTraverser f ScalarTypeBase dim1 als1 dims als2 traverseScalarType _ _ _ (Prim t) = pure $ Prim t traverseScalarType f g h (Record fs) = Record <$> traverse (traverseType f g h) fs-traverseScalarType f g h (TypeVar als u t args) =- TypeVar <$> h als <*> pure u <*> f t <*> traverse (traverseTypeArg f g) args+traverseScalarType f g h (TypeVar als t args) =+ TypeVar <$> h als <*> f t <*> traverse (traverseTypeArg f g) args traverseScalarType f g h (Arrow als v u t1 (RetType dims t2)) = Arrow <$> h als <*> pure v <*> pure u <*> traverseType f g pure t1- <*> (RetType dims <$> traverseType f g h t2)+ <*> (RetType dims <$> traverseType f g pure t2) traverseScalarType f g h (Sum cs) = Sum <$> (traverse . traverse) (traverseType f g h) cs traverseType :: Applicative f => TypeTraverser f TypeBase dim1 als1 dims als2-traverseType f g h (Array als u shape et) =- Array <$> h als <*> pure u <*> traverse g shape <*> traverseScalarType f g pure et+traverseType f g h (Array als shape et) =+ Array <$> h als <*> traverse g shape <*> traverseScalarType f g pure et traverseType f g h (Scalar t) = Scalar <$> traverseScalarType f g h t @@ -332,58 +312,64 @@ (dim1 -> f dim2) -> TypeArg dim1 -> f (TypeArg dim2)-traverseTypeArg _ g (TypeArgDim d loc) =- TypeArgDim <$> g d <*> pure loc-traverseTypeArg f g (TypeArgType t loc) =- TypeArgType <$> traverseType f g pure t <*> pure loc+traverseTypeArg _ g (TypeArgDim d) =+ TypeArgDim <$> g d+traverseTypeArg f g (TypeArgType t) =+ TypeArgType <$> traverseType f g pure t instance ASTMappable StructType where- astMap tv = traverseType (astMap tv) (astMap tv) pure+ astMap tv = traverseType (astMap tv) (mapOnExp tv) pure -instance ASTMappable PatType where- astMap tv = traverseType (astMap tv) (astMap tv) (astMap tv)+instance ASTMappable ParamType where+ astMap tv = traverseType (astMap tv) (mapOnExp tv) pure -instance ASTMappable StructRetType where- astMap tv (RetType ext t) = RetType ext <$> astMap tv t+instance ASTMappable (TypeBase Size Uniqueness) where+ astMap tv = traverseType (astMap tv) (mapOnExp tv) pure -instance ASTMappable PatRetType where+instance ASTMappable ResRetType where astMap tv (RetType ext t) = RetType ext <$> astMap tv t -instance ASTMappable (IdentBase Info VName) where+instance ASTMappable (IdentBase Info VName StructType) where astMap tv (Ident name (Info t) loc) =- Ident <$> mapOnName tv name <*> (Info <$> mapOnPatType tv t) <*> pure loc+ Ident <$> mapOnName tv name <*> (Info <$> mapOnStructType tv t) <*> pure loc instance ASTMappable (SizeBinder VName) where astMap tv (SizeBinder name loc) = SizeBinder <$> mapOnName tv name <*> pure loc -instance ASTMappable (PatBase Info VName) where- astMap tv (Id name (Info t) loc) =- Id <$> mapOnName tv name <*> (Info <$> mapOnPatType tv t) <*> pure loc- astMap tv (TuplePat pats loc) =- TuplePat <$> mapM (astMap tv) pats <*> pure loc- astMap tv (RecordPat fields loc) =- RecordPat <$> mapM (traverse $ astMap tv) fields <*> pure loc- astMap tv (PatParens pat loc) =- PatParens <$> astMap tv pat <*> pure loc- astMap tv (PatAscription pat t loc) =- PatAscription <$> astMap tv pat <*> astMap tv t <*> pure loc- astMap tv (Wildcard (Info t) loc) =- Wildcard <$> (Info <$> mapOnPatType tv t) <*> pure loc- astMap tv (PatLit v (Info t) loc) =- PatLit v <$> (Info <$> mapOnPatType tv t) <*> pure loc- astMap tv (PatConstr n (Info t) ps loc) =- PatConstr n <$> (Info <$> mapOnPatType tv t) <*> mapM (astMap tv) ps <*> pure loc- astMap tv (PatAttr attr p loc) =- PatAttr attr <$> astMap tv p <*> pure loc+traversePat :: Monad m => (t1 -> m t2) -> PatBase Info VName t1 -> m (PatBase Info VName t2)+traversePat f (Id name (Info t) loc) =+ Id name <$> (Info <$> f t) <*> pure loc+traversePat f (TuplePat pats loc) =+ TuplePat <$> mapM (traversePat f) pats <*> pure loc+traversePat f (RecordPat fields loc) =+ RecordPat <$> mapM (traverse $ traversePat f) fields <*> pure loc+traversePat f (PatParens pat loc) =+ PatParens <$> traversePat f pat <*> pure loc+traversePat f (PatAscription pat t loc) =+ PatAscription <$> traversePat f pat <*> pure t <*> pure loc+traversePat f (Wildcard (Info t) loc) =+ Wildcard <$> (Info <$> f t) <*> pure loc+traversePat f (PatLit v (Info t) loc) =+ PatLit v <$> (Info <$> f t) <*> pure loc+traversePat f (PatConstr n (Info t) ps loc) =+ PatConstr n <$> (Info <$> f t) <*> mapM (traversePat f) ps <*> pure loc+traversePat f (PatAttr attr p loc) =+ PatAttr attr <$> traversePat f p <*> pure loc +instance ASTMappable (PatBase Info VName StructType) where+ astMap tv = traversePat $ mapOnStructType tv++instance ASTMappable (PatBase Info VName ParamType) where+ astMap tv = traversePat $ mapOnParamType tv+ instance ASTMappable (FieldBase Info VName) where astMap tv (RecordFieldExplicit name e loc) = RecordFieldExplicit name <$> mapOnExp tv e <*> pure loc astMap tv (RecordFieldImplicit name t loc) = RecordFieldImplicit <$> mapOnName tv name- <*> traverse (mapOnPatType tv) t+ <*> traverse (mapOnStructType tv) t <*> pure loc instance ASTMappable (CaseBase Info VName) where@@ -418,7 +404,7 @@ bareField (RecordFieldImplicit name _ loc) = RecordFieldImplicit name NoInfo loc -barePat :: PatBase Info VName -> PatBase NoInfo VName+barePat :: PatBase Info VName t -> PatBase NoInfo VName t barePat (TuplePat ps loc) = TuplePat (map barePat ps) loc barePat (RecordPat fs loc) = RecordPat (map (fmap barePat) fs) loc barePat (PatParens p loc) = PatParens (barePat p) loc@@ -443,8 +429,26 @@ bareCase :: CaseBase Info VName -> CaseBase NoInfo VName bareCase (CasePat pat e loc) = CasePat (barePat pat) (bareExp e) loc +bareSizeExp :: SizeExp Info VName -> SizeExp NoInfo VName+bareSizeExp (SizeExp e loc) = SizeExp (bareExp e) loc+bareSizeExp (SizeExpAny loc) = SizeExpAny loc+ bareTypeExp :: TypeExp Info VName -> TypeExp NoInfo VName-bareTypeExp = undefined+bareTypeExp (TEVar qn loc) = TEVar qn loc+bareTypeExp (TEParens te loc) = TEParens (bareTypeExp te) loc+bareTypeExp (TETuple tys loc) = TETuple (map bareTypeExp tys) loc+bareTypeExp (TERecord fs loc) = TERecord (map (second bareTypeExp) fs) loc+bareTypeExp (TEArray size ty loc) = TEArray (bareSizeExp size) (bareTypeExp ty) loc+bareTypeExp (TEUnique ty loc) = TEUnique (bareTypeExp ty) loc+bareTypeExp (TEApply ty ta loc) = TEApply (bareTypeExp ty) (bareTypeArgExp ta) loc+ where+ bareTypeArgExp (TypeArgExpSize size) =+ TypeArgExpSize $ bareSizeExp size+ bareTypeArgExp (TypeArgExpType tya) =+ TypeArgExpType $ bareTypeExp tya+bareTypeExp (TEArrow arg tya tyr loc) = TEArrow arg (bareTypeExp tya) (bareTypeExp tyr) loc+bareTypeExp (TESum cs loc) = TESum (map (second $ map bareTypeExp) cs) loc+bareTypeExp (TEDim names ty loc) = TEDim names (bareTypeExp ty) loc -- | Remove all annotations from an expression, but retain the -- name/scope information.@@ -461,6 +465,7 @@ bareExp (RecordLit fields loc) = RecordLit (map bareField fields) loc bareExp (ArrayLit els _ loc) = ArrayLit (map bareExp els) NoInfo loc bareExp (Ascript e te loc) = Ascript (bareExp e) (bareTypeExp te) loc+bareExp (Coerce e te _ loc) = Coerce (bareExp e) (bareTypeExp te) NoInfo loc bareExp (Negate x loc) = Negate (bareExp x) loc bareExp (Not x loc) = Not (bareExp x) loc bareExp (Update src slice v loc) =@@ -521,8 +526,6 @@ loc Range start next end loc -> Range (bareExp start) (fmap bareExp next) (fmap bareExp end) loc- Coerce e te loc ->- Coerce (bareExp e) (bareTypeExp te) loc Index arr slice loc -> Index (bareExp arr) (map bareDimIndex slice) loc bareExp (Attr attr e loc) =
src/Language/Futhark/TypeChecker.hs view
@@ -20,7 +20,7 @@ import Control.Monad import Control.Monad.Except-import Data.Bifunctor (first, second)+import Data.Bifunctor import Data.Either import Data.List qualified as L import Data.Map.Strict qualified as M@@ -51,7 +51,7 @@ UncheckedProg -> (Warnings, Either TypeError (FileModule, VNameSource)) checkProg files src name prog =- runTypeM initialEnv files' name src $ checkProgM prog+ runTypeM initialEnv files' name src checkSizeExp $ checkProgM prog where files' = M.map fileEnv $ M.fromList files @@ -67,7 +67,7 @@ UncheckedExp -> (Warnings, Either TypeError ([TypeParam], Exp)) checkExp files src env e =- second (fmap fst) $ runTypeM env files' (mkInitialImport "") src $ checkOneExp e+ second (fmap fst) $ runTypeM env files' (mkInitialImport "") src checkSizeExp $ checkOneExp e where files' = M.map fileEnv $ M.fromList files @@ -84,7 +84,7 @@ (Warnings, Either TypeError (Env, Dec, VNameSource)) checkDec files src env name d = second (fmap massage) $- runTypeM env files' name src $ do+ runTypeM env files' name src checkSizeExp $ do (_, env', d') <- checkOneDec d pure (env' <> env, d') where@@ -103,7 +103,7 @@ ModExpBase NoInfo Name -> (Warnings, Either TypeError (MTy, ModExpBase Info VName)) checkModExp files src env me =- second (fmap fst) . runTypeM env files' (mkInitialImport "") src $ do+ second (fmap fst) . runTypeM env files' (mkInitialImport "") src checkSizeExp $ do (_abs, mty, me') <- checkOneModExp me pure (mty, me') where@@ -199,7 +199,7 @@ { envTypeTable = M.singleton v $ TypeAbbr l [] . RetType [] . Scalar $- TypeVar () Nonunique (qualName v) []+ TypeVar mempty (qualName v) [] } checkTypeDecl ::@@ -207,7 +207,7 @@ TypeM ([VName], TypeExp Info VName, StructType, Liftedness) checkTypeDecl te = do (te', svars, RetType dims st, l) <- checkTypeExp te- pure (svars ++ dims, te', st, l)+ pure (svars ++ dims, te', toStruct st, l) -- In this function, after the recursion, we add the Env of the -- current Spec *after* the one that is returned from the recursive@@ -264,7 +264,7 @@ envTypeTable = M.singleton name' $ TypeAbbr l ps' . RetType [] . Scalar $- TypeVar () Nonunique (qualName name') $+ TypeVar mempty (qualName name') $ map typeParamToArg ps' } (abstypes, env, specs') <- localEnv tenv $ checkSpecs specs@@ -554,7 +554,7 @@ checkTypeParams tps $ \tps' -> do (te', svars, RetType dims t, l') <- bindingTypeParams tps' $ checkTypeExp te - let (witnessed, _) = determineSizeWitnesses t+ let (witnessed, _) = determineSizeWitnesses $ toStruct t case L.find (`S.notMember` witnessed) svars of Just _ -> typeError (locOf te) mempty . withIndexLink "anonymous-nonconstructive" $@@ -562,9 +562,9 @@ Nothing -> pure () - let elab_t = RetType (svars ++ dims) t+ let elab_t = RetType (svars ++ dims) $ toStruct t - let used_dims = freeInType t+ let used_dims = fvVars $ freeInType t case filter ((`S.notMember` used_dims) . typeParamName) $ filter isSizeParam tps' of [] -> pure ()@@ -603,16 +603,11 @@ TypeBind name' l tps' te' (Info elab_t) doc loc ) -entryPoint :: [Pat] -> Maybe (TypeExp Info VName) -> StructRetType -> EntryPoint-entryPoint params orig_ret_te (RetType ret orig_ret) =+entryPoint :: [Pat ParamType] -> Maybe (TypeExp Info VName) -> ResRetType -> EntryPoint+entryPoint params orig_ret_te (RetType _ret orig_ret) = EntryPoint (map patternEntry params ++ more_params) rettype' where- (more_params, rettype') = onRetType orig_ret_te $ first extToAny orig_ret-- -- Since the entry point type is not a RetType but just a plain- -- StructType, we have to remove any existentially bound sizes.- extToAny (NamedSize v) | qualLeaf v `elem` ret = AnySize Nothing- extToAny d = d+ (more_params, rettype') = onRetType orig_ret_te $ toStruct orig_ret patternEntry (PatParens p _) = patternEntry p@@ -628,10 +623,10 @@ pname (Named v) = baseName v pname Unnamed = "_" onRetType (Just (TEArrow p t1_te t2_te _)) (Scalar (Arrow _ _ _ t1 (RetType _ t2))) =- let (xs, y) = onRetType (Just t2_te) t2+ let (xs, y) = onRetType (Just t2_te) $ toStruct t2 in (EntryParam (maybe "_" baseName p) (EntryType t1 (Just t1_te)) : xs, y) onRetType _ (Scalar (Arrow _ p _ t1 (RetType _ t2))) =- let (xs, y) = onRetType Nothing t2+ let (xs, y) = onRetType Nothing $ toStruct t2 in (EntryParam (pname p) (EntryType t1 Nothing) : xs, y) onRetType te t = ([], EntryType t te)@@ -639,9 +634,9 @@ checkEntryPoint :: SrcLoc -> [TypeParam] ->- [Pat] ->+ [Pat ParamType] -> Maybe (TypeExp Info VName) ->- StructRetType ->+ ResRetType -> TypeM () checkEntryPoint loc tparams params maybe_tdecl rettype | any isTypeParam tparams =@@ -657,13 +652,19 @@ "Entry point functions may not be higher-order." | sizes_only_in_ret <- S.fromList (map typeParamName tparams)- `S.intersection` freeInType rettype'- `S.difference` foldMap freeInType param_ts,+ `S.intersection` fvVars (freeInType rettype')+ `S.difference` foldMap (fvVars . freeInType) param_ts, not $ S.null sizes_only_in_ret = typeError loc mempty $ withIndexLink "size-polymorphic-entry" "Entry point functions must not be size-polymorphic in their return type."+ | (constructive, _) <- foldMap (determineSizeWitnesses . toStruct) param_ts,+ Just p <- L.find (flip S.notMember constructive . typeParamName) tparams =+ typeError p mempty . withIndexLink "nonconstructive-entry" $+ "Entry point size parameter "+ <> pretty p+ <> " only used non-constructively." | p : _ <- filter nastyParameter params = warn p $ "Entry point parameter\n"@@ -679,7 +680,7 @@ where (RetType _ rettype_t) = rettype (rettype_params, rettype') = unfoldFunType rettype_t- param_ts = map patternStructType params ++ map snd rettype_params+ param_ts = map patternType params ++ rettype_params checkValBind :: ValBindBase NoInfo Name -> TypeM (Env, ValBind) checkValBind (ValBind entry fname maybe_tdecl NoInfo tparams params body doc attrs loc) = do@@ -732,7 +733,7 @@ nastyType' (Just te') _ | niceTypeExp te' = False nastyType' _ t' = nastyType t' -nastyParameter :: Pat -> Bool+nastyParameter :: Pat ParamType -> Bool nastyParameter p = nastyType (patternType p) && not (ascripted p) where ascripted (PatAscription _ te _) = niceTypeExp te
+ src/Language/Futhark/TypeChecker/Consumption.hs view
@@ -0,0 +1,972 @@+-- | Check that a value definition does not violate any consumption+-- constraints.+module Language.Futhark.TypeChecker.Consumption+ ( checkValDef,+ )+where++import Control.Monad+import Control.Monad.Reader+import Control.Monad.State+import Data.Bifoldable+import Data.Bifunctor+import Data.DList qualified as DL+import Data.Foldable+import Data.List qualified as L+import Data.List.NonEmpty qualified as NE+import Data.Map.Strict qualified as M+import Data.Maybe+import Data.Set qualified as S+import Futhark.Util.Pretty hiding (space)+import Language.Futhark+import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Monad (Notes, TypeError (..), withIndexLink)+import Prelude hiding (mod)++type Names = S.Set VName++-- | A variable that is aliased. Can be still in-scope, or have gone+-- out of scope and be free. In the latter case, it behaves more like+-- an equivalence class. See uniqueness-error18.fut for an example of+-- why this is necessary.+data Alias+ = AliasBound {aliasVar :: VName}+ | AliasFree {aliasVar :: VName}+ deriving (Eq, Ord, Show)++instance Pretty Alias where+ pretty (AliasBound v) = prettyName v+ pretty (AliasFree v) = "~" <> prettyName v++instance Pretty (S.Set Alias) where+ pretty = braces . commasep . map pretty . S.toList++-- | The set of in-scope variables that are being aliased.+boundAliases :: Aliases -> S.Set VName+boundAliases = S.map aliasVar . S.filter bound+ where+ bound AliasBound {} = True+ bound AliasFree {} = False++-- | Aliases for a type, which is a set of the variables that are+-- aliased.+type Aliases = S.Set Alias++type TypeAliases = TypeBase Size Aliases++-- | @t \`setAliases\` als@ returns @t@, but with @als@ substituted for+-- any already present aliases.+setAliases :: TypeBase dim asf -> ast -> TypeBase dim ast+setAliases t = addAliases t . const++-- | @t \`addAliases\` f@ returns @t@, but with any already present+-- aliases replaced by @f@ applied to that aliases.+addAliases ::+ TypeBase dim asf ->+ (asf -> ast) ->+ TypeBase dim ast+addAliases = flip second++aliases :: TypeAliases -> Aliases+aliases = bifoldMap (const mempty) id++setFieldAliases :: TypeAliases -> [Name] -> TypeAliases -> TypeAliases+setFieldAliases ve_als (x : xs) (Scalar (Record fs)) =+ Scalar $ Record $ M.adjust (setFieldAliases ve_als xs) x fs+setFieldAliases ve_als _ _ = ve_als++data Entry a+ = Consumable {entryAliases :: a}+ | Nonconsumable {entryAliases :: a}+ deriving (Eq, Ord, Show)++instance Functor Entry where+ fmap f (Consumable als) = Consumable $ f als+ fmap f (Nonconsumable als) = Nonconsumable $ f als++data CheckEnv = CheckEnv+ { envVtable :: M.Map VName (Entry TypeAliases),+ -- | Location of the definition we are checking.+ envLoc :: Loc+ }++-- | A description of where an artificial compiler-generated+-- intermediate name came from.+data NameReason+ = -- | Name is the result of a function application.+ NameAppRes (Maybe (QualName VName)) SrcLoc+ | NameLoopRes SrcLoc++nameReason :: SrcLoc -> NameReason -> Doc a+nameReason loc (NameAppRes Nothing apploc) =+ "result of application at" <+> pretty (locStrRel loc apploc)+nameReason loc (NameAppRes fname apploc) =+ "result of applying"+ <+> dquotes (pretty fname)+ <+> parens ("at" <+> pretty (locStrRel loc apploc))+nameReason loc (NameLoopRes apploc) =+ "result of loop at" <+> pretty (locStrRel loc apploc)++type Consumed = M.Map VName Loc++data CheckState = CheckState+ { stateConsumed :: Consumed,+ stateErrors :: DL.DList TypeError,+ stateNames :: M.Map VName NameReason,+ stateCounter :: Int+ }++newtype CheckM a = CheckM (ReaderT CheckEnv (State CheckState) a)+ deriving+ ( Functor,+ Applicative,+ Monad,+ MonadReader CheckEnv,+ MonadState CheckState+ )++runCheckM :: Loc -> CheckM a -> (a, [TypeError])+runCheckM loc (CheckM m) =+ let (a, s) = runState (runReaderT m env) initial_state+ in (a, DL.toList (stateErrors s))+ where+ env =+ CheckEnv+ { envVtable = mempty,+ envLoc = loc+ }+ initial_state =+ CheckState+ { stateConsumed = mempty,+ stateErrors = mempty,+ stateNames = mempty,+ stateCounter = 0+ }++describeVar :: VName -> CheckM (Doc a)+describeVar v = do+ loc <- asks envLoc+ gets $+ maybe ("variable" <+> dquotes (prettyName v)) (nameReason (srclocOf loc))+ . M.lookup v+ . stateNames++noConsumable :: CheckM a -> CheckM a+noConsumable = local $ \env -> env {envVtable = M.map f $ envVtable env}+ where+ f = Nonconsumable . entryAliases++addError :: Located loc => loc -> Notes -> Doc () -> CheckM ()+addError loc notes e = modify $ \s ->+ s {stateErrors = DL.snoc (stateErrors s) (TypeError (locOf loc) notes e)}++incCounter :: CheckM Int+incCounter =+ state $ \s -> (stateCounter s, s {stateCounter = stateCounter s + 1})++returnAliased :: Name -> SrcLoc -> CheckM ()+returnAliased name loc =+ addError loc mempty . withIndexLink "return-aliased" $+ "Unique-typed return value is aliased to"+ <+> dquotes (prettyName name) <> ", which is not consumable."++uniqueReturnAliased :: SrcLoc -> CheckM ()+uniqueReturnAliased loc =+ addError loc mempty . withIndexLink "unique-return-aliased" $+ "A unique-typed component of the return value is aliased to some other component."++checkReturnAlias :: SrcLoc -> [Pat ParamType] -> ResType -> TypeAliases -> CheckM ()+checkReturnAlias loc params rettp =+ foldM_ (checkReturnAlias' params) S.empty . returnAliases rettp+ where+ checkReturnAlias' params' seen (Unique, names) = do+ when (any (`S.member` S.map snd seen) $ S.toList names) $+ uniqueReturnAliased loc+ notAliasesParam params' names+ pure $ seen `S.union` tag Unique names+ checkReturnAlias' _ seen (Nonunique, names) = do+ when (any (`S.member` seen) $ S.toList $ tag Unique names) $+ uniqueReturnAliased loc+ pure $ seen `S.union` tag Nonunique names++ notAliasesParam params' names =+ forM_ params' $ \p ->+ let consumedNonunique (v, t) =+ not (consumableParamType t) && (v `S.member` names)+ in case find consumedNonunique $ patternMap p of+ Just (v, _) ->+ returnAliased (baseName v) loc+ Nothing ->+ pure ()++ tag u = S.map (u,)++ returnAliases (Scalar (Record ets1)) (Scalar (Record ets2)) =+ concat $ M.elems $ M.intersectionWith returnAliases ets1 ets2+ returnAliases expected got =+ [(uniqueness expected, S.map aliasVar $ aliases got)]++ consumableParamType (Array u _ _) = u == Consume+ consumableParamType (Scalar Prim {}) = True+ consumableParamType (Scalar (TypeVar u _ _)) = u == Consume+ consumableParamType (Scalar (Record fs)) = all consumableParamType fs+ consumableParamType (Scalar (Sum fs)) = all (all consumableParamType) fs+ consumableParamType (Scalar Arrow {}) = False++unscope :: [VName] -> Aliases -> Aliases+unscope bound = S.map f+ where+ f (AliasFree v) = AliasFree v+ f (AliasBound v) = if v `elem` bound then AliasFree v else AliasBound v++-- | Figure out the aliases of each bound name in a pattern.+matchPat :: Pat t -> TypeAliases -> DL.DList (VName, (t, TypeAliases))+matchPat (PatParens p _) t = matchPat p t+matchPat (TuplePat ps _) t+ | Just ts <- isTupleRecord t = mconcat $ zipWith matchPat ps ts+matchPat (RecordPat fs1 _) (Scalar (Record fs2)) =+ mconcat $+ zipWith+ matchPat+ (map snd (sortFields (M.fromList fs1)))+ (map snd (sortFields fs2))+matchPat (Id v (Info t) _) als = DL.singleton (v, (t, als))+matchPat (PatAscription p _ _) t = matchPat p t+matchPat (PatConstr v _ ps _) (Scalar (Sum cs))+ | Just ts <- M.lookup v cs = mconcat $ zipWith matchPat ps ts+matchPat TuplePat {} _ = mempty+matchPat RecordPat {} _ = mempty+matchPat PatConstr {} _ = mempty+matchPat Wildcard {} _ = mempty+matchPat PatLit {} _ = mempty+matchPat (PatAttr _ p _) t = matchPat p t++bindingPat ::+ Pat StructType ->+ TypeAliases ->+ CheckM (a, TypeAliases) ->+ CheckM (a, TypeAliases)+bindingPat p t = fmap (second (second (unscope (patNames p)))) . local bind+ where+ bind env =+ env+ { envVtable =+ foldr (uncurry M.insert) (envVtable env) (fmap f (matchPat p t))+ }+ where+ f (v, (_, als)) = (v, Consumable $ second (S.insert (AliasBound v)) als)++bindingParam :: Pat ParamType -> CheckM (a, TypeAliases) -> CheckM (a, TypeAliases)+bindingParam p m = do+ mapM_ (noConsumable . bitraverse_ checkExp pure) p+ second (second (unscope (patNames p))) <$> local bind m+ where+ bind env =+ env+ { envVtable =+ foldr (uncurry M.insert) (envVtable env) (fmap f (patternMap p))+ }+ f (v, t)+ | diet t == Consume = (v, Consumable $ t `setAliases` S.singleton (AliasBound v))+ | otherwise = (v, Nonconsumable $ t `setAliases` S.singleton (AliasBound v))++bindingIdent :: Diet -> Ident StructType -> CheckM (a, TypeAliases) -> CheckM (a, TypeAliases)+bindingIdent d (Ident v (Info t) _) =+ fmap (second (second (unscope [v]))) . local bind+ where+ bind env = env {envVtable = M.insert v t' (envVtable env)}+ d' = case d of+ Consume -> Consumable+ Observe -> Nonconsumable+ t' = d' $ t `setAliases` S.singleton (AliasBound v)++bindingParams :: [Pat ParamType] -> CheckM (a, TypeAliases) -> CheckM (a, TypeAliases)+bindingParams params m =+ noConsumable $+ second (second (unscope (foldMap patNames params)))+ <$> foldr bindingParam m params++bindingLoopForm :: LoopFormBase Info VName -> CheckM (a, TypeAliases) -> CheckM (a, TypeAliases)+bindingLoopForm (For ident _) m = bindingIdent Observe ident m+bindingLoopForm (ForIn pat _) m = bindingParam pat' m+ where+ pat' = fmap (second (const Observe)) pat+bindingLoopForm While {} m = m++bindingFun :: VName -> TypeAliases -> CheckM a -> CheckM a+bindingFun v t = local $ \env ->+ env {envVtable = M.insert v (Nonconsumable t) (envVtable env)}++checkIfConsumed :: Loc -> Aliases -> CheckM ()+checkIfConsumed rloc als = do+ cons <- gets stateConsumed+ let bad v = fmap (v,) $ v `M.lookup` cons+ forM_ (mapMaybe (bad . aliasVar) $ S.toList als) $ \(v, wloc) -> do+ v' <- describeVar v+ addError rloc mempty . withIndexLink "use-after-consume" $+ "Using"+ <+> v' <> ", but this was consumed at"+ <+> pretty (locStrRel rloc wloc) <> ". (Possibly through aliases.)"++consumed :: Consumed -> CheckM ()+consumed vs = modify $ \s -> s {stateConsumed = stateConsumed s <> vs}++consumeAliases :: Loc -> Aliases -> CheckM ()+consumeAliases loc als = do+ vtable <- asks envVtable+ let isBad v =+ case v `M.lookup` vtable of+ Just (Nonconsumable {}) -> True+ Just _ -> False+ Nothing -> True+ checkIfConsumable (AliasBound v)+ | isBad v = do+ v' <- describeVar v+ addError loc mempty . withIndexLink "not-consumable" $+ "Consuming" <+> v' <> ", which is not consumable."+ checkIfConsumable _ = pure ()+ mapM_ checkIfConsumable $ S.toList als+ checkIfConsumed loc als+ consumed als'+ where+ als' = M.fromList $ map ((,loc) . aliasVar) $ S.toList als++consume :: Loc -> VName -> StructType -> CheckM ()+consume loc v t =+ consumeAliases loc . aliases =<< observeVar loc v t++-- | Observe the given name here and return its aliases.+observeVar :: Loc -> VName -> StructType -> CheckM TypeAliases+observeVar loc v t = do+ als <-+ asks $ \env ->+ maybe (isGlobal (envVtable env)) isLocal $+ M.lookup v (envVtable env)+ checkIfConsumed loc (aliases als)+ pure als+ where+ isLocal = entryAliases++ -- Handling globals is tricky. For arrays and such, we do want to+ -- track their aliases. We do not want to track the aliases of+ -- functions. However, array bindings that are *polymorphic*+ -- should be treated like functions. However, we do not have+ -- access to the original binding information here. To avoid+ -- having to plumb that all the way here, we infer that an array+ -- binding is a polymorphic instantiation if its size contains any+ -- locally bound names.+ isGlobal vtable+ | isInstantiation vtable t = second (const mempty) t+ | otherwise = selfAlias $ second (const mempty) t++ isInstantiation vtable =+ any (`M.member` vtable) . fvVars . freeInType++ selfAlias (Array als shape et) = Array (S.insert (AliasBound v) als) shape et+ selfAlias (Scalar st) = Scalar $ selfAlias' st+ selfAlias' (TypeVar als tn args) = TypeVar als tn args -- #1675 FIXME+ selfAlias' (Record fs) = Record $ fmap selfAlias fs+ selfAlias' (Sum fs) = Sum $ fmap (map selfAlias) fs+ selfAlias' et@Arrow {} = et+ selfAlias' et@Prim {} = et++-- Capture any newly consumed variables that occur during the provided action.+contain :: CheckM a -> CheckM (a, Consumed)+contain m = do+ prev_cons <- gets stateConsumed+ x <- m+ new_cons <- gets $ (`M.difference` prev_cons) . stateConsumed+ modify $ \s -> s {stateConsumed = prev_cons}+ pure (x, new_cons)++-- | The two types are assumed to be approximately structurally equal,+-- but not necessarily regarding sizes. Combines aliases and prefers+-- other information from first argument.+combineAliases :: TypeAliases -> TypeAliases -> TypeAliases+combineAliases (Array als1 et1 shape1) t2 =+ Array (als1 <> aliases t2) et1 shape1+combineAliases (Scalar (TypeVar als1 tv1 targs1)) t2 =+ Scalar $ TypeVar (als1 <> aliases t2) tv1 targs1+combineAliases (Scalar (Record ts1)) (Scalar (Record ts2))+ | length ts1 == length ts2,+ L.sort (M.keys ts1) == L.sort (M.keys ts2) =+ Scalar $ Record $ M.intersectionWith combineAliases ts1 ts2+combineAliases+ (Scalar (Arrow als1 mn1 d1 pt1 (RetType dims1 rt1)))+ (Scalar (Arrow als2 _ _ _ (RetType _ _))) =+ Scalar (Arrow (als1 <> als2) mn1 d1 pt1 (RetType dims1 rt1))+combineAliases (Scalar (Sum cs1)) (Scalar (Sum cs2))+ | length cs1 == length cs2,+ L.sort (M.keys cs1) == L.sort (M.keys cs2) =+ Scalar $ Sum $ M.intersectionWith (zipWith combineAliases) cs1 cs2+combineAliases (Scalar (Prim t)) _ = Scalar $ Prim t+combineAliases t1 t2 =+ error $ "combineAliases invalid args: " ++ show (t1, t2)++-- An alias inhibits uniqueness if it is used in disjoint values.+aliasesMultipleTimes :: TypeAliases -> Names+aliasesMultipleTimes = S.fromList . map fst . filter ((> 1) . snd) . M.toList . delve+ where+ delve (Scalar (Record fs)) =+ foldl' (M.unionWith (+)) mempty $ map delve $ M.elems fs+ delve t =+ M.fromList $ zip (map aliasVar $ S.toList (aliases t)) $ repeat (1 :: Int)++consumingParams :: [Pat ParamType] -> Names+consumingParams =+ S.fromList . map fst . filter ((== Consume) . diet . snd) . foldMap patternMap++arrayAliases :: TypeAliases -> Aliases+arrayAliases (Array als _ _) = als+arrayAliases (Scalar Prim {}) = mempty+arrayAliases (Scalar (Record fs)) = foldMap arrayAliases fs+arrayAliases (Scalar (TypeVar als _ _)) = als+arrayAliases (Scalar Arrow {}) = mempty+arrayAliases (Scalar (Sum fs)) =+ mconcat $ concatMap (map arrayAliases) $ M.elems fs++overlapCheck :: (Pretty src, Pretty ve) => Loc -> (src, TypeAliases) -> (ve, TypeAliases) -> CheckM ()+overlapCheck loc (src, src_als) (ve, ve_als) =+ when (any (`S.member` aliases src_als) (aliases ve_als)) $+ addError loc mempty $+ "Source array for in-place update"+ </> indent 2 (pretty src)+ </> "might alias update value"+ </> indent 2 (pretty ve)+ </> "Hint: use"+ <+> dquotes "copy"+ <+> "to remove aliases from the value."++inferReturnUniqueness :: [Pat ParamType] -> ResType -> TypeAliases -> ResType+inferReturnUniqueness [] ret _ = ret `setUniqueness` Nonunique+inferReturnUniqueness params ret ret_als = delve ret ret_als+ where+ forbidden = aliasesMultipleTimes ret_als+ consumings = consumingParams params+ delve (Scalar (Record fs1)) (Scalar (Record fs2)) =+ Scalar $ Record $ M.intersectionWith delve fs1 fs2+ delve (Scalar (Sum cs1)) (Scalar (Sum cs2)) =+ Scalar $ Sum $ M.intersectionWith (zipWith delve) cs1 cs2+ delve t t_als+ | all (`S.member` consumings) $ boundAliases (arrayAliases t_als),+ not $ any ((`S.member` forbidden) . aliasVar) (aliases t_als) =+ t `setUniqueness` Unique+ | otherwise =+ t `setUniqueness` Nonunique++checkSubExps :: ASTMappable e => e -> CheckM e+checkSubExps = astMap identityMapper {mapOnExp = fmap fst . checkExp}++noAliases :: Exp -> CheckM (Exp, TypeAliases)+noAliases e = do+ e' <- checkSubExps e+ pure (e', second (const mempty) (typeOf e))++aliasParts :: TypeAliases -> [Aliases]+aliasParts (Scalar (Record ts)) = foldMap aliasParts $ M.elems ts+aliasParts t = [aliases t]++noSelfAliases :: Loc -> TypeAliases -> CheckM ()+noSelfAliases loc = foldM_ check mempty . aliasParts+ where+ check seen als = do+ when (any (`S.member` seen) als) $+ addError loc mempty . withIndexLink "self-aliases-arg" $+ "Argument passed for consuming parameter is self-aliased."+ pure $ als <> seen++consumeAsNeeded :: Loc -> ParamType -> TypeAliases -> CheckM ()+consumeAsNeeded loc (Scalar (Record fs1)) (Scalar (Record fs2)) =+ sequence_ $ M.elems $ M.intersectionWith (consumeAsNeeded loc) fs1 fs2+consumeAsNeeded loc pt t =+ when (diet pt == Consume) $ consumeAliases loc $ aliases t++checkArg :: ParamType -> Exp -> CheckM (Exp, TypeAliases)+checkArg p_t e = do+ ((e', e_als), e_cons) <- contain $ checkExp e+ consumed e_cons+ let e_t = typeOf e'+ when (e_cons /= mempty && not (orderZero e_t)) $+ addError (locOf e) mempty $+ "Argument of functional type"+ </> indent 2 (pretty e_t)+ </> "contains consumption, which is not allowed."+ when (diet p_t == Consume) $ do+ noSelfAliases (locOf e) e_als+ consumeAsNeeded (locOf e) p_t e_als+ pure (e', e_als)++-- | @returnType appres ret_type arg_diet arg_type@ gives result of applying+-- an argument the given types to a function with the given return+-- type, consuming the argument with the given diet.+returnType :: Aliases -> ResType -> Diet -> TypeAliases -> TypeAliases+returnType _ (Array Unique et shape) _ _ =+ Array mempty et shape+returnType appres (Array Nonunique et shape) Consume _ =+ Array appres et shape+returnType appres (Array Nonunique et shape) Observe arg =+ Array (appres <> aliases arg) et shape+returnType _ (Scalar (TypeVar Unique t targs)) _ _ =+ Scalar $ TypeVar mempty t targs+returnType appres (Scalar (TypeVar Nonunique t targs)) Consume _ =+ Scalar $ TypeVar appres t targs+returnType appres (Scalar (TypeVar Nonunique t targs)) Observe arg =+ Scalar $ TypeVar (appres <> aliases arg) t targs+returnType appres (Scalar (Record fs)) d arg =+ Scalar $ Record $ fmap (\et -> returnType appres et d arg) fs+returnType _ (Scalar (Prim t)) _ _ =+ Scalar $ Prim t+returnType appres (Scalar (Arrow _ v pd t1 (RetType dims t2))) Consume _ =+ Scalar $ Arrow appres v pd t1 $ RetType dims t2+returnType appres (Scalar (Arrow _ v pd t1 (RetType dims t2))) Observe arg =+ Scalar $ Arrow (appres <> aliases arg) v pd t1 $ RetType dims t2+returnType appres (Scalar (Sum cs)) d arg =+ Scalar $ Sum $ (fmap . fmap) (\et -> returnType appres et d arg) cs++applyArg :: TypeAliases -> TypeAliases -> TypeAliases+applyArg (Scalar (Arrow closure_als _ d _ (RetType _ rettype))) arg_als =+ returnType closure_als rettype d arg_als+applyArg t _ = error $ "applyArg: " <> show t++boundFreeInExp :: Exp -> CheckM (M.Map VName TypeAliases)+boundFreeInExp e = do+ vtable <- asks envVtable+ pure $+ M.mapMaybe (fmap entryAliases) . M.fromSet (`M.lookup` vtable) $+ fvVars (freeInExp e)++-- Loops are tricky because we want to infer the uniqueness of their+-- parameters. This is pretty unusual: we do not do this for ordinary+-- functions.+type Loop = (Pat ParamType, Exp, LoopFormBase Info VName, Exp)++-- | Mark bindings of consumed names as Consume.+updateParamDiet :: Names -> Pat ParamType -> Pat ParamType+updateParamDiet cons = recurse+ where+ recurse (Wildcard (Info t) wloc) =+ Wildcard (Info $ t `setUniqueness` Observe) wloc+ recurse (PatParens p ploc) =+ PatParens (recurse p) ploc+ recurse (PatAttr attr p ploc) =+ PatAttr attr (recurse p) ploc+ recurse (Id name (Info t) iloc)+ | name `S.member` cons =+ let t' = t `setUniqueness` Consume+ in Id name (Info t') iloc+ | otherwise =+ let t' = t `setUniqueness` Observe+ in Id name (Info t') iloc+ recurse (TuplePat pats ploc) =+ TuplePat (map recurse pats) ploc+ recurse (RecordPat fs ploc) =+ RecordPat (map (fmap recurse) fs) ploc+ recurse (PatAscription p t ploc) =+ PatAscription p t ploc+ recurse p@PatLit {} = p+ recurse (PatConstr n t ps ploc) =+ PatConstr n t (map recurse ps) ploc++convergeLoopParam :: Loc -> Pat ParamType -> Names -> TypeAliases -> CheckM (Pat ParamType)+convergeLoopParam loop_loc param body_cons body_als = do+ let -- Make the pattern Consume where needed.+ param' = updateParamDiet (S.filter (`elem` patNames param) body_cons) param++ -- Check that the new values of consumed merge parameters do not+ -- alias something bound outside the loop, AND that anything+ -- returned for a unique merge parameter does not alias anything+ -- else returned.+ let checkMergeReturn (Id pat_v (Info pat_v_t) patloc) t = do+ let free_als = S.filter (`notElem` patNames param) $ boundAliases (aliases t)+ when (diet pat_v_t == Consume) $ forM_ free_als $ \v ->+ lift . addError loop_loc mempty $+ "Return value for consuming loop parameter"+ <+> dquotes (prettyName pat_v)+ <+> "aliases"+ <+> dquotes (prettyName v) <> "."+ (cons, obs) <- get+ unless (S.null $ aliases t `S.intersection` cons) $+ lift . addError loop_loc mempty $+ "Return value for loop parameter"+ <+> dquotes (prettyName pat_v)+ <+> "aliases other consumed loop parameter."+ when+ ( diet pat_v_t == Consume+ && not (S.null (aliases t `S.intersection` (cons <> obs)))+ )+ $ lift . addError loop_loc mempty+ $ "Return value for consuming loop parameter"+ <+> dquotes (prettyName pat_v)+ <+> "aliases previously returned value."+ if diet pat_v_t == Consume+ then put (cons <> aliases t, obs)+ else put (cons, obs <> aliases t)++ pure $ Id pat_v (Info pat_v_t) patloc+ checkMergeReturn (Wildcard (Info pat_v_t) patloc) _ =+ pure $ Wildcard (Info pat_v_t) patloc+ checkMergeReturn (PatParens p _) t =+ checkMergeReturn p t+ checkMergeReturn (PatAscription p _ _) t =+ checkMergeReturn p t+ checkMergeReturn (RecordPat pfs patloc) (Scalar (Record tfs)) =+ RecordPat . M.toList <$> sequence pfs' <*> pure patloc+ where+ pfs' = M.intersectionWith checkMergeReturn (M.fromList pfs) tfs+ checkMergeReturn (TuplePat pats patloc) t+ | Just ts <- isTupleRecord t =+ TuplePat <$> zipWithM checkMergeReturn pats ts <*> pure patloc+ checkMergeReturn p _ =+ pure p++ (param'', (param_cons, _)) <-+ runStateT (checkMergeReturn param' body_als) (mempty, mempty)++ let body_cons' = body_cons <> S.map aliasVar param_cons+ if body_cons' == body_cons && patternType param'' == patternType param+ then pure param'+ else convergeLoopParam loop_loc param'' body_cons' body_als++checkLoop :: Loc -> Loop -> CheckM (Loop, TypeAliases)+checkLoop loop_loc (param, arg, form, body) = do+ form' <- checkSubExps form+ -- We pretend that every part of the loop parameter has a consuming+ -- diet, as we need to allow consumption in the body, which we then+ -- use to infer the proper diet of the parameter.+ ((body', body_cons), body_als) <-+ noConsumable+ . bindingParam (fmap (second (const Consume)) param)+ . bindingLoopForm form'+ $ do+ ((body', body_als), body_cons) <- contain $ checkExp body+ pure ((body', body_cons), body_als)+ param' <- convergeLoopParam loop_loc param (M.keysSet body_cons) body_als++ let param_t = patternType param'+ ((arg', arg_als), arg_cons) <- contain $ checkArg param_t arg+ consumed arg_cons+ free_bound <- boundFreeInExp body++ let bad = any (`M.member` arg_cons) . boundAliases . aliases . snd+ forM_ (filter bad $ M.toList free_bound) $ \(v, _) -> do+ v' <- describeVar v+ addError loop_loc mempty $+ "Loop body uses"+ <+> v' <> " (or an alias),"+ </> "but this is consumed by the initial loop argument."++ v <- VName "internal_loop_result" <$> incCounter+ modify $ \s -> s {stateNames = M.insert v (NameLoopRes (srclocOf loop_loc)) $ stateNames s}++ let loopt =+ funType [param'] (RetType [] $ paramToRes param_t)+ `setAliases` S.singleton (AliasFree v)+ pure+ ( (param', arg', form', body'),+ applyArg loopt arg_als `combineAliases` body_als+ )++checkFuncall ::+ Foldable f =>+ SrcLoc ->+ Maybe (QualName VName) ->+ TypeAliases ->+ f TypeAliases ->+ CheckM TypeAliases+checkFuncall loc fname f_als args_als = do+ v <- VName "internal_app_result" <$> incCounter+ modify $ \s -> s {stateNames = M.insert v (NameAppRes fname loc) $ stateNames s}+ pure $ foldl applyArg (second (S.insert (AliasFree v)) f_als) args_als++checkExp :: Exp -> CheckM (Exp, TypeAliases)+-- First we have the complicated cases.++--+checkExp (AppExp (Apply f args loc) appres) = do+ -- Note Futhark uses right-to-left evaluation of applications.+ (args', args_als) <- NE.unzip . NE.reverse <$> traverse checkArg' (NE.reverse args)+ (f', f_als) <- checkExp f+ res_als <- checkFuncall loc (fname f) f_als args_als+ pure (AppExp (Apply f' args' loc) appres, res_als)+ where+ fname (Var v _ _) = Just v+ fname (AppExp (Apply e _ _) _) = fname e+ fname _ = Nothing+ checkArg' (Info (d, p), e) = do+ (e', e_als) <- checkArg (second (const d) (typeOf e)) e+ pure ((Info (d, p), e'), e_als)++--+checkExp (AppExp (DoLoop sparams pat args form body loc) appres) = do+ ((pat', args', form', body'), als) <- checkLoop (locOf loc) (pat, args, form, body)+ pure+ ( AppExp (DoLoop sparams pat' args' form' body' loc) appres,+ als+ )++--+checkExp (AppExp (LetPat sizes p e body loc) appres) = do+ ((e', e_als), e_cons) <- contain $ checkExp e+ consumed e_cons+ let e_t = typeOf e'+ when (e_cons /= mempty && not (orderZero e_t)) $+ addError (locOf e) mempty $+ "Let-bound expression of higher-order type"+ </> indent 2 (pretty e_t)+ </> "contains consumption, which is not allowed."+ bindingPat p e_als $ do+ (body', body_als) <- checkExp body+ pure+ ( AppExp (LetPat sizes p e' body' loc) appres,+ body_als+ )++--+checkExp (AppExp (If cond te fe loc) appres) = do+ (cond', _) <- checkExp cond+ ((te', te_als), te_cons) <- contain $ checkExp te+ ((fe', fe_als), fe_cons) <- contain $ checkExp fe+ let all_cons = te_cons <> fe_cons+ notConsumed = not . (`M.member` all_cons) . aliasVar+ comb_als = second (S.filter notConsumed) $ te_als `combineAliases` fe_als+ consumed all_cons+ pure+ ( AppExp (If cond' te' fe' loc) appres,+ appResType (unInfo appres) `setAliases` mempty `combineAliases` comb_als+ )++--+checkExp (AppExp (Match cond cs loc) appres) = do+ (cond', cond_als) <- checkExp cond+ ((cs', cs_als), cs_cons) <-+ first NE.unzip . NE.unzip <$> mapM (checkCase cond_als) cs+ let all_cons = fold cs_cons+ notConsumed = not . (`M.member` all_cons) . aliasVar+ comb_als = second (S.filter notConsumed) $ foldl1 combineAliases cs_als+ consumed all_cons+ pure+ ( AppExp (Match cond' cs' loc) appres,+ appResType (unInfo appres) `setAliases` mempty `combineAliases` comb_als+ )+ where+ checkCase cond_als (CasePat p body caseloc) =+ contain $ bindingPat p cond_als $ do+ (body', body_als) <- checkExp body+ pure (CasePat p body' caseloc, body_als)++--+checkExp (AppExp (LetFun fname (typarams, params, te, Info (RetType ext ret), funbody) letbody loc) appres) = do+ ((ret', funbody'), ftype) <- bindingParams params $ do+ -- Throw away the consumption - it can refer only to the parameters+ -- anyway.+ ((funbody', funbody_als), _body_cons) <- contain $ checkExp funbody+ checkReturnAlias loc params ret funbody_als+ checkGlobalAliases loc params funbody_als+ free_bound <- boundFreeInExp funbody+ let ret' = inferReturnUniqueness params ret funbody_als+ als = foldMap aliases (M.elems free_bound)+ ftype = funType params (RetType ext ret') `setAliases` als+ pure ((ret', funbody'), ftype)+ (letbody', letbody_als) <- bindingFun fname ftype $ checkExp letbody+ pure+ ( AppExp (LetFun fname (typarams, params, te, Info (RetType ext ret'), funbody') letbody' loc) appres,+ letbody_als+ )++--+checkExp (AppExp (BinOp (op, oploc) opt (x, xp) (y, yp) loc) appres) = do+ op_als <- observeVar (locOf oploc) (qualLeaf op) (unInfo opt)+ let at1 : at2 : _ = fst $ unfoldFunType op_als+ (x', x_als) <- checkArg at1 x+ (y', y_als) <- checkArg at2 y+ res_als <- checkFuncall loc (Just op) op_als [x_als, y_als]+ pure+ ( AppExp (BinOp (op, oploc) opt (x', xp) (y', yp) loc) appres,+ res_als+ )++--+checkExp e@(Lambda params body te (Info (RetType ext ret)) loc) =+ bindingParams params $ do+ -- Throw away the consumption - it can refer only to the parameters+ -- anyway.+ ((body', body_als), _body_cons) <- contain $ checkExp body+ checkReturnAlias loc params ret body_als+ checkGlobalAliases loc params body_als+ free_bound <- boundFreeInExp e+ let ret' = inferReturnUniqueness params ret body_als+ als = foldMap aliases (M.elems free_bound)+ ftype = funType params (RetType ext ret') `setAliases` als+ pure+ ( Lambda params body' te (Info (RetType ext ret')) loc,+ ftype+ )++--+checkExp (AppExp (LetWith dst src slice ve body loc) appres) = do+ src_als <- observeVar (locOf dst) (identName src) (unInfo $ identType src)+ slice' <- checkSubExps slice+ (ve', ve_als) <- checkExp ve+ consume (locOf src) (identName src) (unInfo (identType src))+ overlapCheck (locOf ve) (src, src_als) (ve', ve_als)+ (body', body_als) <- bindingIdent Consume dst $ checkExp body+ pure (AppExp (LetWith dst src slice' ve' body' loc) appres, body_als)++--+checkExp (Update src slice ve loc) = do+ slice' <- checkSubExps slice+ (ve', ve_als) <- checkExp ve+ (src', src_als) <- checkExp src+ overlapCheck (locOf ve) (src', src_als) (ve', ve_als)+ consumeAliases (locOf loc) $ aliases src_als+ pure (Update src' slice' ve' loc, second (const mempty) src_als)++-- Cases that simply propagate aliases directly.+checkExp (Var v (Info t) loc) = do+ als <- observeVar (locOf loc) (qualLeaf v) t+ checkIfConsumed (locOf loc) (aliases als)+ pure (Var v (Info t) loc, als)+checkExp (OpSection v (Info t) loc) = do+ als <- observeVar (locOf loc) (qualLeaf v) t+ checkIfConsumed (locOf loc) (aliases als)+ pure (OpSection v (Info t) loc, als)+checkExp (OpSectionLeft op ftype arg arginfo retinfo loc) = do+ let (_, Info (pn, pt2)) = arginfo+ (Info ret, _) = retinfo+ als <- observeVar (locOf loc) (qualLeaf op) (unInfo ftype)+ (arg', arg_als) <- checkExp arg+ pure+ ( OpSectionLeft op ftype arg' arginfo retinfo loc,+ Scalar $ Arrow (aliases arg_als <> aliases als) pn (diet pt2) (toStruct pt2) ret+ )+checkExp (OpSectionRight op ftype arg arginfo retinfo loc) = do+ let (Info (pn, pt2), _) = arginfo+ Info ret = retinfo+ als <- observeVar (locOf loc) (qualLeaf op) (unInfo ftype)+ (arg', arg_als) <- checkExp arg+ pure+ ( OpSectionRight op ftype arg' arginfo retinfo loc,+ Scalar $ Arrow (aliases arg_als <> aliases als) pn (diet pt2) (toStruct pt2) ret+ )+checkExp (IndexSection slice t loc) = do+ slice' <- checkSubExps slice+ pure (IndexSection slice' t loc, unInfo t `setAliases` mempty)+checkExp (ProjectSection fs t loc) = do+ pure (ProjectSection fs t loc, unInfo t `setAliases` mempty)+checkExp (Coerce e te t loc) = do+ (e', e_als) <- checkExp e+ pure (Coerce e' te t loc, e_als)+checkExp (Ascript e te loc) = do+ (e', e_als) <- checkExp e+ pure (Ascript e' te loc, e_als)+checkExp (AppExp (Index v slice loc) appres) = do+ (v', v_als) <- checkExp v+ slice' <- checkSubExps slice+ pure+ ( AppExp (Index v' slice' loc) appres,+ appResType (unInfo appres) `setAliases` aliases v_als+ )+checkExp (Assert e1 e2 t loc) = do+ (e1', _) <- checkExp e1+ (e2', e2_als) <- checkExp e2+ pure (Assert e1' e2' t loc, e2_als)+checkExp (Parens e loc) = do+ (e', e_als) <- checkExp e+ pure (Parens e' loc, e_als)+checkExp (QualParens v e loc) = do+ (e', e_als) <- checkExp e+ pure (QualParens v e' loc, e_als)+checkExp (Attr attr e loc) = do+ (e', e_als) <- checkExp e+ pure (Attr attr e' loc, e_als)+checkExp (Project name e t loc) = do+ (e', e_als) <- checkExp e+ pure+ ( Project name e' t loc,+ case e_als of+ Scalar (Record fs)+ | Just name_als <- M.lookup name fs -> name_als+ _ -> error $ "checkExp Project: bad type " <> prettyString e_als+ )+checkExp (TupLit es loc) = do+ (es', es_als) <- mapAndUnzipM checkExp es+ pure (TupLit es' loc, Scalar $ tupleRecord es_als)+checkExp (Constr name es t loc) = do+ (es', es_als) <- mapAndUnzipM checkExp es+ pure+ ( Constr name es' t loc,+ case unInfo t of+ Scalar (Sum cs) ->+ Scalar . Sum . M.insert name es_als $+ M.map (map (`setAliases` mempty)) cs+ t' -> error $ "checkExp Constr: bad type " <> prettyString t'+ )+checkExp (RecordUpdate src fields ve t loc) = do+ (src', src_als) <- checkExp src+ (ve', ve_als) <- checkExp ve+ pure+ ( RecordUpdate src' fields ve' t loc,+ setFieldAliases ve_als fields src_als+ )+checkExp (RecordLit fs loc) = do+ (fs', fs_als) <- mapAndUnzipM checkField fs+ pure (RecordLit fs' loc, Scalar $ Record $ M.fromList fs_als)+ where+ checkField (RecordFieldExplicit name e floc) = do+ (e', e_als) <- checkExp e+ pure (RecordFieldExplicit name e' floc, (name, e_als))+ checkField (RecordFieldImplicit name t floc) = do+ name_als <- observeVar (locOf floc) name $ unInfo t+ pure (RecordFieldImplicit name t floc, (baseName name, name_als))++-- Cases that create alias-free values.+checkExp e@(AppExp Range {} _) = noAliases e+checkExp e@IntLit {} = noAliases e+checkExp e@FloatLit {} = noAliases e+checkExp e@Literal {} = noAliases e+checkExp e@StringLit {} = noAliases e+checkExp e@ArrayLit {} = noAliases e+checkExp e@Negate {} = noAliases e+checkExp e@Not {} = noAliases e+checkExp e@Hole {} = noAliases e++checkGlobalAliases :: SrcLoc -> [Pat ParamType] -> TypeAliases -> CheckM ()+checkGlobalAliases loc params body_t = do+ vtable <- asks envVtable+ let global = flip M.notMember vtable+ unless (null params) $ forM_ (boundAliases $ arrayAliases body_t) $ \v ->+ when (global v) . addError loc mempty . withIndexLink "alias-free-variable" $+ "Function result aliases the free variable "+ <> dquotes (prettyName v)+ <> "."+ </> "Use"+ <+> dquotes "copy"+ <+> "to break the aliasing."++-- | Type-check a value definition. This also infers a new return+-- type that may be more unique than previously.+checkValDef ::+ (VName, [Pat ParamType], Exp, ResRetType, Maybe (TypeExp Info VName), SrcLoc) ->+ ((Exp, ResRetType), [TypeError])+checkValDef (_fname, params, body, RetType ext ret, retdecl, loc) = runCheckM (locOf loc) $ do+ fmap fst . bindingParams params $ do+ (body', body_als) <- checkExp body+ checkReturnAlias loc params ret body_als+ checkGlobalAliases loc params body_als+ -- If the user did not provide an annotation (meaning the return+ -- type is fully inferred), we infer the uniqueness. Otherwise,+ -- we go with whatever they wanted. This lets the user define+ -- non-unique return types even if the body actually has no+ -- aliases.+ ret' <- case retdecl of+ Just retdecl' -> do+ when (null params && unique ret) $+ addError retdecl' mempty "A top-level constant cannot have a unique type."+ pure $ RetType ext ret+ Nothing ->+ pure $ RetType ext $ inferReturnUniqueness params ret body_als+ pure+ ( (body', ret'),+ body_als -- Don't matter.+ )+{-# NOINLINE checkValDef #-}
src/Language/Futhark/TypeChecker/Match.hs view
@@ -49,7 +49,7 @@ instance Pretty (Match t) where pretty = pprMatch (-1) -patternToMatch :: Pat -> Match StructType+patternToMatch :: Pat StructType -> Match StructType patternToMatch (Id _ (Info t) _) = MatchWild $ toStruct t patternToMatch (Wildcard (Info t) _) = MatchWild $ toStruct t patternToMatch (PatParens p _) = patternToMatch p@@ -174,7 +174,7 @@ {-# NOINLINE unmatched #-} -- | Find the unmatched cases.-unmatched :: [Pat] -> [Match ()]+unmatched :: [Pat StructType] -> [Match ()] unmatched orig_ps = -- The algorithm may find duplicate example, which we filter away -- here.
src/Language/Futhark/TypeChecker/Modules.hs view
@@ -10,7 +10,6 @@ import Control.Monad import Data.Either-import Data.List (intersect) import Data.Map.Strict qualified as M import Data.Maybe import Data.Ord@@ -106,6 +105,10 @@ substitute v = fromMaybe v $ M.lookup v substs + -- For applySubst and friends.+ subst v =+ ExpSubst . flip sizeFromName mempty . qualName <$> M.lookup v substs+ substituteInMap f m = let (ks, vs) = unzip $ M.toList m in M.fromList $@@ -135,37 +138,31 @@ substituteInTypeParam (TypeParamType l p loc) = TypeParamType l (substitute p) loc - substituteInType :: StructType -> StructType- substituteInType (Scalar (TypeVar () u (QualName qs v) targs)) =- Scalar $- TypeVar () u (QualName (map substitute qs) $ substitute v) $- map substituteInTypeArg targs- substituteInType (Scalar (Prim t)) =- Scalar $ Prim t- substituteInType (Scalar (Record ts)) =- Scalar $ Record $ fmap substituteInType ts- substituteInType (Scalar (Sum ts)) =- Scalar $ Sum $ (fmap . fmap) substituteInType ts- substituteInType (Array () u shape t) =- arrayOf u (substituteInShape shape) (substituteInType $ Scalar t)- substituteInType (Scalar (Arrow als v d1 t1 (RetType dims t2))) =- Scalar $ Arrow als v d1 (substituteInType t1) $ RetType dims $ substituteInType t2+ substituteInScalarType :: ScalarTypeBase Size u -> ScalarTypeBase Size u+ substituteInScalarType (TypeVar u (QualName qs v) targs) =+ TypeVar u (QualName (map substitute qs) $ substitute v) $+ map substituteInTypeArg targs+ substituteInScalarType (Prim t) =+ Prim t+ substituteInScalarType (Record ts) =+ Record $ fmap substituteInType ts+ substituteInScalarType (Sum ts) =+ Sum $ (fmap . fmap) substituteInType ts+ substituteInScalarType (Arrow als v d1 t1 (RetType dims t2)) =+ Arrow als v d1 (substituteInType t1) $ RetType dims $ substituteInType t2 - substituteInShape (Shape ds) =- Shape $ map substituteInDim ds- substituteInDim (NamedSize (QualName qs v)) =- NamedSize $ QualName (map substitute qs) $ substitute v- substituteInDim d = d+ substituteInType :: TypeBase Size u -> TypeBase Size u+ substituteInType (Scalar t) = Scalar $ substituteInScalarType t+ substituteInType (Array u shape t) =+ Array u (substituteInShape shape) $ substituteInScalarType t - substituteInTypeArg (TypeArgDim (NamedSize (QualName qs v)) loc) =- TypeArgDim (NamedSize $ QualName (map substitute qs) $ substitute v) loc- substituteInTypeArg (TypeArgDim (ConstSize x) loc) =- TypeArgDim (ConstSize x) loc- substituteInTypeArg (TypeArgDim (AnySize v) loc) =- TypeArgDim (AnySize v) loc- substituteInTypeArg (TypeArgType t loc) =- TypeArgType (substituteInType t) loc+ substituteInShape (Shape ds) = Shape $ map (applySubst subst) ds + substituteInTypeArg (TypeArgDim e) =+ TypeArgDim $ applySubst subst e+ substituteInTypeArg (TypeArgType t) =+ TypeArgType $ substituteInType t+ mtyTypeAbbrs :: MTy -> M.Map VName TypeBinding mtyTypeAbbrs (MTy _ mod) = modTypeAbbrs mod @@ -190,7 +187,7 @@ StructType -> TypeM (QualName VName, TySet, Env) refineEnv loc tset env tname ps t- | Just (tname', TypeAbbr _ cur_ps (RetType _ (Scalar (TypeVar () _ (QualName qs v) _)))) <-+ | Just (tname', TypeAbbr _ cur_ps (RetType _ (Scalar (TypeVar _ (QualName qs v) _)))) <- findTypeDef tname (ModEnv env), QualName (qualQuals tname') v `M.member` tset = if paramsMatch cur_ps ps@@ -366,7 +363,7 @@ </> indent 2 (ppTypeAbbr abs (QualName quals name) spec_t) ppTypeAbbr :: [VName] -> QualName VName -> (Liftedness, [TypeParam], StructRetType) -> Doc a-ppTypeAbbr abs name (l, ps, RetType [] (Scalar (TypeVar () _ tn args)))+ppTypeAbbr abs name (l, ps, RetType [] (Scalar (TypeVar _ tn args))) | qualLeaf tn `elem` abs, map typeParamToArg ps == args = "type" <> pretty l@@ -389,7 +386,7 @@ Either TypeError (M.Map VName VName) matchMTys orig_mty orig_mty_sig = matchMTys'- (M.map (SizeSubst . NamedSize) $ resolveMTyNames orig_mty orig_mty_sig)+ (M.map (ExpSubst . flip sizeFromName mempty) $ resolveMTyNames orig_mty orig_mty_sig) [] orig_mty orig_mty_sig@@ -534,21 +531,22 @@ -- if we have a value of an abstract type 't [n]', then there is -- an array of size 'n' somewhere inside. when (M.member spec_name abs_subst_to_type) $- case S.toList (mustBeExplicitInType (retType t)) `intersect` map typeParamName ps of+ case filter+ (`S.notMember` fst (determineSizeWitnesses (retType t)))+ (map typeParamName $ filter isSizeParam ps) of [] -> pure () d : _ -> Left . TypeError loc mempty $ "Type" </> indent 2 (ppTypeAbbr [] (QualName quals name) (l, ps, t)) </> textwrap "cannot be made abstract because size parameter"- </> dquotes (prettyName d)- </> textwrap "is not used as an array size in the definition."+ </> indent 2 (prettyName d)+ </> textwrap "is not used constructively as an array size in the definition." let spec_t' = applySubst (`M.lookup` abs_subst_to_type) spec_t nonrigid = ps <> map (`TypeParamDim` mempty) (retDims t) case doUnification loc spec_ps nonrigid (retType spec_t') (retType t) of- Right t'- | noSizes t' `subtypeOf` noSizes (retType spec_t') -> pure (spec_name, name)+ Right _ -> pure (spec_name, name) _ -> nomatch spec_t' where nomatch spec_t' =@@ -578,18 +576,15 @@ </> indent 2 (ppValBind (QualName quals spec_name) spec_v) </> "but module provides" </> indent 2 (ppValBind (QualName quals spec_name) v)- </> fromMaybe mempty problem+ </> problem - matchValBinding :: Loc -> BoundV -> BoundV -> Maybe (Maybe (Doc ()))+ matchValBinding :: Loc -> BoundV -> BoundV -> Maybe (Doc ()) matchValBinding loc (BoundV spec_tps orig_spec_t) (BoundV tps orig_t) = do case doUnification loc spec_tps tps (toStruct orig_spec_t) (toStruct orig_t) of Left (TypeError _ notes msg) ->- Just $ Just $ msg <> pretty notes- -- Even if they unify, we still have to verify the uniqueness- -- properties.- Right t- | noSizes t `subtypeOf` noSizes orig_spec_t -> Nothing- | otherwise -> Just Nothing+ Just $ msg <> pretty notes+ Right _ ->+ Nothing ppValBind v (BoundV tps t) = "val"@@ -615,7 +610,7 @@ let a_abbrs = mtyTypeAbbrs a_mty isSub v = case M.lookup v a_abbrs of Just abbr -> Just $ substFromAbbr abbr- _ -> Just $ SizeSubst $ NamedSize $ qualName v+ _ -> Just $ ExpSubst $ sizeFromName (qualName v) mempty type_subst = M.mapMaybe isSub p_subst body_mty' = substituteTypesInMTy (`M.lookup` type_subst) body_mty (body_mty'', body_subst) <- newNamesForMTy body_mty'
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -23,6 +23,7 @@ Notes, aNote, MonadTypeChecker (..),+ TypeState (stateNameSource), checkName, checkAttr, badOnLeft,@@ -162,7 +163,8 @@ contextImportName :: ImportName, -- | Currently type-checking at the top level? If false, we are -- inside a module.- contextAtTopLevel :: Bool+ contextAtTopLevel :: Bool,+ contextCheckExp :: UncheckedExp -> TypeM Exp } data TypeState = TypeState@@ -205,10 +207,11 @@ ImportTable -> ImportName -> VNameSource ->+ (UncheckedExp -> TypeM Exp) -> TypeM a -> (Warnings, Either TypeError (a, VNameSource))-runTypeM env imports fpath src (TypeM m) = do- let ctx = Context env imports fpath True+runTypeM env imports fpath src checker (TypeM m) = do+ let ctx = Context env imports fpath True checker s = TypeState src mempty 0 case runExcept $ runStateT (runReaderT m ctx) s of Left (ws, e) -> (ws, Left e)@@ -272,6 +275,7 @@ -- expressions and declarations. class Monad m => MonadTypeChecker m where warn :: Located loc => loc -> Doc () -> m ()+ warnings :: Warnings -> m () newName :: VName -> m VName newID :: Name -> m VName@@ -284,19 +288,9 @@ lookupType :: SrcLoc -> QualName Name -> m (QualName VName, [TypeParam], StructRetType, Liftedness) lookupMod :: SrcLoc -> QualName Name -> m (QualName VName, Mod)- lookupVar :: SrcLoc -> QualName Name -> m (QualName VName, PatType)+ lookupVar :: SrcLoc -> QualName Name -> m (QualName VName, StructType) - checkNamedSize :: SrcLoc -> QualName Name -> m (QualName VName)- checkNamedSize loc v = do- (v', t) <- lookupVar loc v- case t of- Scalar (Prim (Signed Int64)) -> pure v'- _ ->- typeError loc mempty $- "Sizes must have type i64, but"- <+> dquotes (pretty v)- <+> "has type:"- </> pretty t+ checkExpForSize :: UncheckedExp -> m Exp typeError :: Located loc => loc -> Notes -> Doc () -> m a @@ -314,11 +308,11 @@ bindNameMap mapping body instance MonadTypeChecker TypeM where+ warnings ws =+ modify $ \s -> s {stateWarnings = stateWarnings s <> ws}+ warn loc problem =- modify $ \s ->- s- { stateWarnings = stateWarnings s <> singleWarning (srclocOf loc) problem- }+ warnings $ singleWarning (srclocOf loc) problem newName v = do s <- get@@ -375,10 +369,13 @@ Just t' -> pure ( qn',- fromStruct $- qualifyTypeVars outer_env mempty qs t'+ qualifyTypeVars outer_env mempty qs t' ) + checkExpForSize e = do+ checker <- asks contextCheckExp+ checker e+ typeError loc notes s = throwError $ TypeError (locOf loc) notes s -- | Extract from a type a first-order type.@@ -421,14 +418,14 @@ S.Set VName -> TypeBase Size as -> TypeBase Size as- onType except (Array as u shape et) =- Array as u (fmap (onDim except) shape) (onScalar except et)+ onType except (Array u shape et) =+ Array u (fmap (onDim except) shape) (onScalar except et) onType except (Scalar t) = Scalar $ onScalar except t onScalar _ (Prim t) = Prim t- onScalar except (TypeVar as u qn targs) =- TypeVar as u (qual except qn) (map (onTypeArg except) targs)+ onScalar except (TypeVar u qn targs) =+ TypeVar u (qual except qn) (map (onTypeArg except) targs) onScalar except (Record m) = Record $ M.map (onType except) m onScalar except (Sum m) =@@ -440,12 +437,12 @@ Named p' -> S.insert p' except Unnamed -> except - onTypeArg except (TypeArgDim d loc) =- TypeArgDim (onDim except d) loc- onTypeArg except (TypeArgType t loc) =- TypeArgType (onType except t) loc+ onTypeArg except (TypeArgDim d) =+ TypeArgDim $ onDim except d+ onTypeArg except (TypeArgType t) =+ TypeArgType $ onType except t - onDim except (NamedSize qn) = NamedSize $ qual except qn+ onDim except (Var qn typ loc) = Var (qual except qn) typ loc onDim _ d = d qual except (QualName orig_qs name)@@ -464,7 +461,7 @@ name `M.member` envVtable env || isJust (find matches $ M.elems (envTypeTable env)) where- matches (TypeAbbr _ _ (RetType _ (Scalar (TypeVar _ _ (QualName x_qs name') _)))) =+ matches (TypeAbbr _ _ (RetType _ (Scalar (TypeVar _ (QualName x_qs name') _)))) = null x_qs && name == name' matches _ = False reachable (q : qs') name env
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -8,1703 +8,1707 @@ -- will require the programmer to fall back on type annotations. module Language.Futhark.TypeChecker.Terms ( checkOneExp,- checkFunDef,- )-where--import Control.Monad-import Control.Monad.Except-import Control.Monad.Reader-import Control.Monad.State-import Data.Bitraversable-import Data.Either-import Data.List (find, foldl', genericLength, partition)-import Data.List.NonEmpty qualified as NE-import Data.Map.Strict qualified as M-import Data.Maybe-import Data.Set qualified as S-import Futhark.Util (mapAccumLM)-import Futhark.Util.Pretty hiding (space)-import Language.Futhark-import Language.Futhark.Primitive (intByteSize)-import Language.Futhark.Traversals-import Language.Futhark.TypeChecker.Match-import Language.Futhark.TypeChecker.Monad hiding (BoundV)-import Language.Futhark.TypeChecker.Terms.DoLoop-import Language.Futhark.TypeChecker.Terms.Monad-import Language.Futhark.TypeChecker.Terms.Pat-import Language.Futhark.TypeChecker.Types-import Language.Futhark.TypeChecker.Unify-import Prelude hiding (mod)--overloadedTypeVars :: Constraints -> Names-overloadedTypeVars = mconcat . map f . M.elems- where- f (_, HasFields _ fs _) = mconcat $ map typeVars $ M.elems fs- f _ = mempty----- Basic checking---- | Determine if the two types are identical, ignoring uniqueness.--- Mismatched dimensions are turned into fresh rigid type variables.--- Causes a 'TypeError' if they fail to match, and otherwise returns--- one of them.-unifyBranchTypes :: SrcLoc -> PatType -> PatType -> TermTypeM (PatType, [VName])-unifyBranchTypes loc t1 t2 =- onFailure (CheckingBranches (toStruct t1) (toStruct t2)) $- unifyMostCommon (mkUsage loc "unification of branch results") t1 t2--unifyBranches :: SrcLoc -> Exp -> Exp -> TermTypeM (PatType, [VName])-unifyBranches loc e1 e2 = do- e1_t <- expTypeFully e1- e2_t <- expTypeFully e2- unifyBranchTypes loc e1_t e2_t--sliceShape ::- Maybe (SrcLoc, Rigidity) ->- Slice ->- TypeBase Size as ->- TermTypeM (TypeBase Size as, [VName])-sliceShape r slice t@(Array als u (Shape orig_dims) et) =- runStateT (setDims <$> adjustDims slice orig_dims) []- where- setDims [] = stripArray (length orig_dims) t- setDims dims' = Array als u (Shape dims') et-- -- If the result is supposed to be a nonrigid size variable, then- -- don't bother trying to create non-existential sizes. This is- -- necessary to make programs type-check without too much- -- ceremony; see e.g. tests/inplace5.fut.- isRigid Rigid {} = True- isRigid _ = False- refine_sizes = maybe False (isRigid . snd) r-- sliceSize orig_d i j stride =- case r of- Just (loc, Rigid _) -> do- (d, ext) <-- lift . extSize loc $- SourceSlice orig_d' (bareExp <$> i) (bareExp <$> j) (bareExp <$> stride)- modify (maybeToList ext ++)- pure d- Just (loc, Nonrigid) ->- lift $ NamedSize . qualName <$> newDimVar loc Nonrigid "slice_dim"- Nothing -> do- v <- lift $ newID "slice_anydim"- modify (v :)- pure $ NamedSize $ qualName v- where- -- The original size does not matter if the slice is fully specified.- orig_d'- | isJust i, isJust j = Nothing- | otherwise = Just orig_d-- adjustDims (DimFix {} : idxes') (_ : dims) =- adjustDims idxes' dims- -- Pat match some known slices to be non-existential.- adjustDims (DimSlice i j stride : idxes') (_ : dims)- | refine_sizes,- maybe True ((== Just 0) . isInt64) i,- Just j' <- maybeDimFromExp =<< j,- maybe True ((== Just 1) . isInt64) stride =- (j' :) <$> adjustDims idxes' dims- adjustDims (DimSlice Nothing Nothing stride : idxes') (d : dims)- | refine_sizes,- maybe True (maybe False ((== 1) . abs) . isInt64) stride =- (d :) <$> adjustDims idxes' dims- adjustDims (DimSlice i j stride : idxes') (d : dims) =- (:) <$> sliceSize d i j stride <*> adjustDims idxes' dims- adjustDims _ dims =- pure dims-sliceShape _ _ t = pure (t, [])----- Main checkers---- The closure of a lambda or local function are those variables that--- it references, and which local to the current top-level function.-lexicalClosure :: [Pat] -> Occurrences -> TermTypeM Aliasing-lexicalClosure params closure = do- vtable <- asks $ scopeVtable . termScope- let isGlobal v = case v `M.lookup` vtable of- Just (BoundV Global _ _) -> True- Just EqualityF {} -> True- Just OverloadedF {} -> True- Just (BoundV Local _ _) -> False- Just (BoundV Nonlocal _ _) -> False- Just WasConsumed {} -> False- Nothing -> False- pure . S.map AliasBound . S.filter (not . isGlobal) $- allOccurring closure S.\\ mconcat (map patNames params)--noAliasesIfOverloaded :: PatType -> TermTypeM PatType-noAliasesIfOverloaded t@(Scalar (TypeVar _ u tn [])) = do- subst <- fmap snd . M.lookup (qualLeaf tn) <$> getConstraints- case subst of- Just Overloaded {} -> pure $ Scalar $ TypeVar mempty u tn []- _ -> pure t-noAliasesIfOverloaded t =- pure t--checkAscript ::- SrcLoc ->- UncheckedTypeExp ->- UncheckedExp ->- TermTypeM (TypeExp Info VName, Exp)-checkAscript loc te e = do- (te', decl_t, _) <- checkTypeExpNonrigid te- e' <- checkExp e- e_t <- toStruct <$> expTypeFully e'-- onFailure (CheckingAscription decl_t e_t) $- unify (mkUsage loc "type ascription") decl_t e_t-- pure (te', e')--checkCoerce ::- SrcLoc ->- UncheckedTypeExp ->- UncheckedExp ->- TermTypeM (TypeExp Info VName, StructType, Exp)-checkCoerce loc te e = do- (te', te_t, ext) <- checkTypeExpNonrigid te- e' <- checkExp e- e_t <- toStruct <$> expTypeFully e'-- te_t_nonrigid <- makeNonExtFresh ext te_t-- onFailure (CheckingAscription te_t e_t) $- unify (mkUsage loc "size coercion") e_t te_t_nonrigid-- -- If the type expression had any anonymous dimensions, these will- -- now be in 'ext'. Those we keep nonrigid and unify with e_t.- -- This ensures that 'x :> [1][]i32' does not make the second- -- dimension unknown. Use of matchDims is sensible because the- -- structure of e_t' will be fully known due to the unification, and- -- te_t because type expressions are complete.- pure (te', te_t, e')- where- makeNonExtFresh ext = bitraverse onDim pure- where- onDim d@(NamedSize v)- | qualLeaf v `elem` ext = pure d- onDim _ = do- v <- newTypeName "coerce"- constrain v . Size Nothing $- mkUsage- loc- "a size coercion where the underlying expression size cannot be determined"- pure $ NamedSize $ qualName v--unscopeType ::- SrcLoc ->- M.Map VName Ident ->- PatType ->- TermTypeM (PatType, [VName])-unscopeType tloc unscoped t = do- (t', m) <- runStateT (traverseDims onDim t) mempty- pure (t' `addAliases` S.map unAlias, M.elems m)- where- onDim bound _ (NamedSize d)- | Just loc <- srclocOf <$> M.lookup (qualLeaf d) unscoped,- not $ qualLeaf d `S.member` bound =- inst loc $ qualLeaf d- onDim _ _ d = pure d-- inst loc d = do- prev <- gets $ M.lookup d- case prev of- Just d' -> pure $ NamedSize $ qualName d'- Nothing -> do- d' <- lift $ newDimVar tloc (Rigid $ RigidOutOfScope loc d) "d"- modify $ M.insert d d'- pure $ NamedSize $ qualName d'-- unAlias (AliasBound v) | v `M.member` unscoped = AliasFree v- unAlias a = a--checkExp :: UncheckedExp -> TermTypeM Exp-checkExp (Literal val loc) =- pure $ Literal val loc-checkExp (Hole _ loc) = do- t <- newTypeVar loc "t"- pure $ Hole (Info t) loc-checkExp (StringLit vs loc) =- pure $ StringLit vs loc-checkExp (IntLit val NoInfo loc) = do- t <- newTypeVar loc "t"- mustBeOneOf anyNumberType (mkUsage loc "integer literal") t- pure $ IntLit val (Info $ fromStruct t) loc-checkExp (FloatLit val NoInfo loc) = do- t <- newTypeVar loc "t"- mustBeOneOf anyFloatType (mkUsage loc "float literal") t- pure $ FloatLit val (Info $ fromStruct t) loc-checkExp (TupLit es loc) =- TupLit <$> mapM checkExp es <*> pure loc-checkExp (RecordLit fs loc) = do- fs' <- evalStateT (mapM checkField fs) mempty-- pure $ RecordLit fs' loc- where- checkField (RecordFieldExplicit f e rloc) = do- errIfAlreadySet f rloc- modify $ M.insert f rloc- RecordFieldExplicit f <$> lift (checkExp e) <*> pure rloc- checkField (RecordFieldImplicit name NoInfo rloc) = do- errIfAlreadySet name rloc- (QualName _ name', t) <- lift $ lookupVar rloc $ qualName name- modify $ M.insert name rloc- pure $ RecordFieldImplicit name' (Info t) rloc-- errIfAlreadySet f rloc = do- maybe_sloc <- gets $ M.lookup f- case maybe_sloc of- Just sloc ->- lift . typeError rloc mempty $- "Field"- <+> dquotes (pretty f)- <+> "previously defined at"- <+> pretty (locStrRel rloc sloc) <> "."- Nothing -> pure ()-checkExp (ArrayLit all_es _ loc) =- -- Construct the result type and unify all elements with it. We- -- only create a type variable for empty arrays; otherwise we use- -- the type of the first element. This significantly cuts down on- -- the number of type variables generated for pathologically large- -- multidimensional array literals.- case all_es of- [] -> do- et <- newTypeVar loc "t"- t <- arrayOfM loc et (Shape [ConstSize 0]) Nonunique- pure $ ArrayLit [] (Info t) loc- e : es -> do- e' <- checkExp e- et <- expType e'- es' <- mapM (unifies "type of first array element" (toStruct et) <=< checkExp) es- et' <- normTypeFully et- t <- arrayOfM loc et' (Shape [ConstSize $ genericLength all_es]) Nonunique- pure $ ArrayLit (e' : es') (Info t) loc-checkExp (AppExp (Range start maybe_step end loc) _) = do- start' <- require "use in range expression" anySignedType =<< checkExp start- start_t <- toStruct <$> expTypeFully start'- maybe_step' <- case maybe_step of- Nothing -> pure Nothing- Just step -> do- let warning = warn loc "First and second element of range are identical, this will produce an empty array."- case (start, step) of- (Literal x _, Literal y _) -> when (x == y) warning- (Var x_name _ _, Var y_name _ _) -> when (x_name == y_name) warning- _ -> pure ()- Just <$> (unifies "use in range expression" start_t =<< checkExp step)-- let unifyRange e = unifies "use in range expression" start_t =<< checkExp e- end' <- traverse unifyRange end-- end_t <- case end' of- DownToExclusive e -> expType e- ToInclusive e -> expType e- UpToExclusive e -> expType e-- -- Special case some ranges to give them a known size.- let dimFromBound = dimFromExp (SourceBound . bareExp)- (dim, retext) <-- case (isInt64 start', isInt64 <$> maybe_step', end') of- (Just 0, Just (Just 1), UpToExclusive end'')- | Scalar (Prim (Signed Int64)) <- end_t ->- dimFromBound end''- (Just 0, Nothing, UpToExclusive end'')- | Scalar (Prim (Signed Int64)) <- end_t ->- dimFromBound end''- (Just 1, Just (Just 2), ToInclusive end'')- | Scalar (Prim (Signed Int64)) <- end_t ->- dimFromBound end''- _ -> do- d <- newDimVar loc (Rigid RigidRange) "range_dim"- pure (NamedSize $ qualName d, Just d)-- t <- arrayOfM loc start_t (Shape [dim]) Nonunique- let res = AppRes (t `setAliases` mempty) (maybeToList retext)-- pure $ AppExp (Range start' maybe_step' end' loc) (Info res)-checkExp (Ascript e te loc) = do- (te', e') <- checkAscript loc te e- pure $ Ascript e' te' loc-checkExp (AppExp (Coerce e te loc) _) = do- (te', te_t, e') <- checkCoerce loc te e- t <- expTypeFully e'- t' <- matchDims (const . const pure) t $ fromStruct te_t- pure $ AppExp (Coerce e' te' loc) (Info $ AppRes t' [])-checkExp (AppExp (BinOp (op, oploc) NoInfo (e1, _) (e2, _) loc) NoInfo) = do- (op', ftype) <- lookupVar oploc op- e1_arg <- checkArg e1- e2_arg <- checkArg e2-- -- Note that the application to the first operand cannot fix any- -- existential sizes, because it must by necessity be a function.- (_, p1_t, rt, p1_ext, _) <- checkApply loc (Just op', 0) ftype e1_arg- (_, p2_t, rt', p2_ext, retext) <- checkApply loc (Just op', 1) rt e2_arg-- pure $- AppExp- ( BinOp- (op', oploc)- (Info ftype)- (argExp e1_arg, Info (toStruct p1_t, p1_ext))- (argExp e2_arg, Info (toStruct p2_t, p2_ext))- loc- )- (Info (AppRes rt' retext))-checkExp (Project k e NoInfo loc) = do- e' <- checkExp e- t <- expType e'- kt <- mustHaveField (mkUsage loc $ docText $ "projection of field " <> dquotes (pretty k)) k t- pure $ Project k e' (Info kt) loc-checkExp (AppExp (If e1 e2 e3 loc) _) =- sequentially checkCond $ \e1' _ -> do- ((e2', e3'), dflow) <- tapOccurrences $ checkExp e2 `alternative` checkExp e3-- (brancht, retext) <- unifyBranches loc e2' e3'- let t' = addAliases brancht $ S.filter $ (`S.notMember` allConsumed dflow) . aliasVar-- zeroOrderType- (mkUsage loc "returning value of this type from 'if' expression")- "type returned from branch"- (toStruct t')-- pure $ AppExp (If e1' e2' e3' loc) (Info $ AppRes t' retext)- where- checkCond = do- e1' <- checkExp e1- let bool = Scalar $ Prim Bool- e1_t <- toStruct <$> expType e1'- onFailure (CheckingRequired [bool] e1_t) $- unify (mkUsage (srclocOf e1') "use as 'if' condition") bool e1_t- pure e1'-checkExp (Parens e loc) =- Parens <$> checkExp e <*> pure loc-checkExp (QualParens (modname, modnameloc) e loc) = do- (modname', mod) <- lookupMod loc modname- case mod of- ModEnv env -> local (`withEnv` qualifyEnv modname' env) $ do- e' <- checkExp e- pure $ QualParens (modname', modnameloc) e' loc- ModFun {} ->- typeError loc mempty . withIndexLink "module-is-parametric" $- "Module" <+> pretty modname <+> " is a parametric module."- where- qualifyEnv modname' env =- env {envNameMap = M.map (qualify' modname') $ envNameMap env}- qualify' modname' (QualName qs name) =- QualName (qualQuals modname' ++ [qualLeaf modname'] ++ qs) name-checkExp (Var qn NoInfo loc) = do- -- The qualifiers of a variable is divided into two parts: first a- -- possibly-empty sequence of module qualifiers, followed by a- -- possible-empty sequence of record field accesses. We use scope- -- information to perform the split, by taking qualifiers off the- -- end until we find a module.-- (qn', t, fields) <- findRootVar (qualQuals qn) (qualLeaf qn)-- foldM checkField (Var qn' (Info t) loc) fields- where- findRootVar qs name =- (whenFound <$> lookupVar loc (QualName qs name)) `catchError` notFound qs name-- whenFound (qn', t) = (qn', t, [])-- notFound qs name err- | null qs = throwError err- | otherwise = do- (qn', t, fields) <-- findRootVar (init qs) (last qs)- `catchError` const (throwError err)- pure (qn', t, fields ++ [name])-- checkField e k = do- t <- expType e- let usage = mkUsage loc $ docText $ "projection of field " <> dquotes (pretty k)- kt <- mustHaveField usage k t- pure $ Project k e (Info kt) loc-checkExp (Negate arg loc) = do- arg' <- require "numeric negation" anyNumberType =<< checkExp arg- pure $ Negate arg' loc-checkExp (Not arg loc) = do- arg' <- require "logical negation" (Bool : anyIntType) =<< checkExp arg- pure $ Not arg' loc-checkExp (AppExp (Apply fe args loc) NoInfo) = do- fe' <- checkExp fe- args' <- mapM (checkArg . snd) args- t <- expType fe'- let fname =- case fe' of- Var v _ _ -> Just v- _ -> Nothing- ((_, exts, rt), args'') <- mapAccumLM (onArg fname) (0, [], t) args'- pure $ AppExp (Apply fe' args'' loc) $ Info $ AppRes rt exts- where- onArg fname (i, all_exts, t) arg' = do- (d1, _, rt, argext, exts) <- checkApply loc (fname, i) t arg'- pure- ( (i + 1, all_exts <> exts, rt),- (Info (d1, argext), argExp arg')- )-checkExp (AppExp (LetPat sizes pat e body loc) _) =- sequentially (checkExp e) $ \e' e_occs -> do- -- Not technically an ascription, but we want the pattern to have- -- exactly the type of 'e'.- t <- expType e'- case anyConsumption e_occs of- Just c ->- zeroOrderType- (mkUsage loc "consumption in right-hand side of 'let'-binding")- ("type computed with consumption at " <> locText (location c))- (toStruct t)- _ -> pure ()-- incLevel . bindingSizes sizes $ \sizes' ->- bindingPat sizes' pat (Ascribed t) $ \pat' -> do- body' <- checkExp body- (body_t, retext) <-- unscopeType loc (sizesMap sizes' <> patternMap pat') =<< expTypeFully body'-- pure $ AppExp (LetPat sizes' pat' e' body' loc) (Info $ AppRes body_t retext)- where- sizesMap = foldMap onSize- onSize size =- M.singleton (sizeName size) $- Ident (sizeName size) (Info (Scalar $ Prim $ Signed Int64)) (srclocOf size)-checkExp (AppExp (LetFun name (tparams, params, maybe_retdecl, NoInfo, e) body loc) _) =- sequentially (checkBinding (name, maybe_retdecl, tparams, params, e, loc)) $- \(tparams', params', maybe_retdecl', rettype, e') closure -> do- closure' <- lexicalClosure params' closure-- bindSpaced [(Term, name)] $ do- name' <- checkName Term name loc-- let ftype = funType params' rettype- entry = BoundV Local tparams' $ ftype `setAliases` closure'- bindF scope =- scope- { scopeVtable =- M.insert name' entry $ scopeVtable scope,- scopeNameMap =- M.insert (Term, name) (qualName name') $- scopeNameMap scope- }- body' <- localScope bindF $ checkExp body-- -- We fake an ident here, but it's OK as it can't be a size- -- anyway.- let fake_ident = Ident name' (Info $ fromStruct ftype) mempty- (body_t, ext) <-- unscopeType loc (M.singleton name' fake_ident)- =<< expTypeFully body'-- pure $- AppExp- ( LetFun- name'- (tparams', params', maybe_retdecl', Info rettype, e')- body'- loc- )- (Info $ AppRes body_t ext)-checkExp (AppExp (LetWith dest src slice ve body loc) _) =- sequentially ((,) <$> checkIdent src <*> checkSlice slice) $ \(src', slice') _ -> do- (t, _) <- newArrayType (srclocOf src) "src" $ sliceDims slice'- unify (mkUsage loc "type of target array") t $ toStruct $ unInfo $ identType src'-- -- Need the fully normalised type here to get the proper aliasing information.- src_t <- normTypeFully $ unInfo $ identType src'-- (elemt, _) <- sliceShape (Just (loc, Nonrigid)) slice' =<< normTypeFully t-- sequentially (unifies "type of target array" (toStruct elemt) =<< checkExp ve) $ \ve' _ -> do- ve_t <- expTypeFully ve'- when (AliasBound (identName src') `S.member` aliases ve_t) $- badLetWithValue src ve loc-- bindingIdent dest (src_t `setAliases` S.empty) $ \dest' -> do- body' <- consuming src' $ checkExp body- (body_t, ext) <-- unscopeType loc (M.singleton (identName dest') dest')- =<< expTypeFully body'- pure $ AppExp (LetWith dest' src' slice' ve' body' loc) (Info $ AppRes body_t ext)-checkExp (Update src slice ve loc) = do- slice' <- checkSlice slice- (t, _) <- newArrayType (srclocOf src) "src" $ sliceDims slice'- (elemt, _) <- sliceShape (Just (loc, Nonrigid)) slice' =<< normTypeFully t-- sequentially (checkExp ve >>= unifies "type of target array" elemt) $ \ve' _ ->- sequentially (checkExp src >>= unifies "type of target array" t) $ \src' _ -> do- src_t <- expTypeFully src'-- let src_als = aliases src_t- ve_t <- expTypeFully ve'- unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue src ve loc-- consume loc src_als- pure $ Update src' slice' ve' loc---- Record updates are a bit hacky, because we do not have row typing--- (yet?). For now, we only permit record updates where we know the--- full type up to the field we are updating.-checkExp (RecordUpdate src fields ve NoInfo loc) = do- src' <- checkExp src- ve' <- checkExp ve- a <- expTypeFully src'- foldM_ (flip $ mustHaveField usage) a fields- ve_t <- expType ve'- updated_t <- updateField fields ve_t =<< expTypeFully src'- pure $ RecordUpdate src' fields ve' (Info updated_t) loc- where- usage = mkUsage loc "record update"- updateField [] ve_t src_t = do- (src_t', _) <- allDimsFreshInType loc Nonrigid "any" src_t- onFailure (CheckingRecordUpdate fields (toStruct src_t') (toStruct ve_t)) $- unify usage (toStruct src_t') (toStruct ve_t)- -- Important that we return ve_t so that we get the right aliases.- pure ve_t- updateField (f : fs) ve_t (Scalar (Record m))- | Just f_t <- M.lookup f m = do- f_t' <- updateField fs ve_t f_t- pure $ Scalar $ Record $ M.insert f f_t' m- updateField _ _ _ =- typeError loc mempty . withIndexLink "record-type-not-known" $- "Full type of"- </> indent 2 (pretty src)- </> textwrap " is not known at this point. Add a type annotation to the original record to disambiguate."-----checkExp (AppExp (Index e slice loc) _) = do- slice' <- checkSlice slice- (t, _) <- newArrayType loc "e" $ sliceDims slice'- e' <- unifies "being indexed at" t =<< checkExp e- -- XXX, the RigidSlice here will be overridden in sliceShape with a proper value.- (t', retext) <-- sliceShape (Just (loc, Rigid (RigidSlice Nothing ""))) slice'- =<< expTypeFully e'-- -- Remove aliases if the result is an overloaded type, because that- -- will certainly not be aliased.- t'' <- noAliasesIfOverloaded t'-- pure $ AppExp (Index e' slice' loc) (Info $ AppRes t'' retext)-checkExp (Assert e1 e2 NoInfo loc) = do- e1' <- require "being asserted" [Bool] =<< checkExp e1- e2' <- checkExp e2- pure $ Assert e1' e2' (Info (prettyText e1)) loc-checkExp (Lambda params body rettype_te NoInfo loc) = do- (params', body', body_t, rettype', info) <-- removeSeminullOccurrences . noUnique . incLevel . bindingParams [] params $ \_ params' -> do- rettype_checked <- traverse checkTypeExpNonrigid rettype_te- let declared_rettype =- case rettype_checked of- Just (_, st, _) -> Just st- Nothing -> Nothing- (body', closure) <-- tapOccurrences $ checkFunBody params' body declared_rettype loc- body_t <- expTypeFully body'-- params'' <- mapM updateTypes params'-- (rettype', rettype_st) <-- case rettype_checked of- Just (te, st, ext) -> do- let st_structural = toStructural st- checkReturnAlias loc st_structural params'' body_t- pure (Just te, RetType ext st)- Nothing -> do- ret <-- inferReturnSizes params'' . toStruct $- inferReturnUniqueness params'' body_t- pure (Nothing, ret)-- closure' <- lexicalClosure params'' closure-- pure (params'', body', body_t, rettype', Info (closure', rettype_st))-- checkGlobalAliases params' body_t loc- verifyFunctionParams Nothing params'-- pure $ Lambda params' body' rettype' info loc- where- -- Inferring the sizes of the return type of a lambda is a lot- -- like let-generalisation. We wish to remove any rigid sizes- -- that were created when checking the body, except for those that- -- are visible in types that existed before we entered the body,- -- are parameters, or are used in parameters.- inferReturnSizes params' ret = do- cur_lvl <- curLevel- let named (Named x, _, _) = Just x- named (Unnamed, _, _) = Nothing- param_names = mapMaybe (named . patternParam) params'- pos_sizes =- sizeNamesPos $ foldFunTypeFromParams params' $ RetType [] ret- hide k (lvl, _) =- lvl >= cur_lvl && k `notElem` param_names && k `S.notMember` pos_sizes-- hidden_sizes <-- S.fromList . M.keys . M.filterWithKey hide <$> getConstraints-- let onDim name- | name `S.member` hidden_sizes = S.singleton name- onDim _ = mempty-- pure $ RetType (S.toList $ foldMap onDim $ freeInType ret) ret-checkExp (OpSection op _ loc) = do- (op', ftype) <- lookupVar loc op- pure $ OpSection op' (Info ftype) loc-checkExp (OpSectionLeft op _ e _ _ loc) = do- (op', ftype) <- lookupVar loc op- e_arg <- checkArg e- (_, t1, rt, argext, retext) <- checkApply loc (Just op', 0) ftype e_arg- case (ftype, rt) of- (Scalar (Arrow _ m1 _ _ _), Scalar (Arrow _ m2 _ t2 rettype)) ->- pure $- OpSectionLeft- op'- (Info ftype)- (argExp e_arg)- (Info (m1, toStruct t1, argext), Info (m2, toStruct t2))- (Info rettype, Info retext)- loc- _ ->- typeError loc mempty $- "Operator section with invalid operator of type" <+> pretty ftype-checkExp (OpSectionRight op _ e _ NoInfo loc) = do- (op', ftype) <- lookupVar loc op- e_arg <- checkArg e- case ftype of- Scalar (Arrow as1 m1 d1 t1 (RetType [] (Scalar (Arrow as2 m2 d2 t2 (RetType dims2 ret))))) -> do- (_, t2', ret', argext, _) <-- checkApply- loc- (Just op', 1)- (Scalar $ Arrow as2 m2 d2 t2 $ RetType [] $ Scalar $ Arrow as1 m1 d1 t1 $ RetType [] ret)- e_arg- pure $- OpSectionRight- op'- (Info ftype)- (argExp e_arg)- (Info (m1, toStruct t1), Info (m2, toStruct t2', argext))- (Info $ RetType dims2 $ addAliases ret (<> aliases ret'))- loc- _ ->- typeError loc mempty $- "Operator section with invalid operator of type" <+> pretty ftype-checkExp (ProjectSection fields NoInfo loc) = do- a <- newTypeVar loc "a"- let usage = mkUsage loc "projection at"- b <- foldM (flip $ mustHaveField usage) a fields- let ft = Scalar $ Arrow mempty Unnamed Observe (toStruct a) $ RetType [] b- pure $ ProjectSection fields (Info ft) loc-checkExp (IndexSection slice NoInfo loc) = do- slice' <- checkSlice slice- (t, _) <- newArrayType loc "e" $ sliceDims slice'- (t', retext) <- sliceShape Nothing slice' t- let ft = Scalar $ Arrow mempty Unnamed Observe t $ RetType retext $ fromStruct t'- pure $ IndexSection slice' (Info ft) loc-checkExp (AppExp (DoLoop _ mergepat mergeexp form loopbody loc) _) = do- ((sparams, mergepat', mergeexp', form', loopbody'), appres) <-- checkDoLoop checkExp (mergepat, mergeexp, form, loopbody) loc- pure $- AppExp- (DoLoop sparams mergepat' mergeexp' form' loopbody' loc)- (Info appres)-checkExp (Constr name es NoInfo loc) = do- t <- newTypeVar loc "t"- es' <- mapM checkExp es- ets <- mapM expTypeFully es'- mustHaveConstr (mkUsage loc "use of constructor") name t (toStruct <$> ets)- -- A sum value aliases *anything* that went into its construction.- let als = foldMap aliases ets- pure $ Constr name es' (Info $ fromStruct t `addAliases` (<> als)) loc-checkExp (AppExp (Match e cs loc) _) =- sequentially (checkExp e) $ \e' _ -> do- mt <- expTypeFully e'- (cs', t, retext) <- checkCases mt cs- zeroOrderType- (mkUsage loc "being returned 'match'")- "type returned from pattern match"- (toStruct t)- pure $ AppExp (Match e' cs' loc) (Info $ AppRes t retext)-checkExp (Attr info e loc) =- Attr <$> checkAttr info <*> checkExp e <*> pure loc--checkCases ::- PatType ->- NE.NonEmpty (CaseBase NoInfo Name) ->- TermTypeM (NE.NonEmpty (CaseBase Info VName), PatType, [VName])-checkCases mt rest_cs =- case NE.uncons rest_cs of- (c, Nothing) -> do- (c', t, retext) <- checkCase mt c- pure (NE.singleton c', t, retext)- (c, Just cs) -> do- (((c', c_t, _), (cs', cs_t, _)), dflow) <-- tapOccurrences $ checkCase mt c `alternative` checkCases mt cs- (brancht, retext) <- unifyBranchTypes (srclocOf c) c_t cs_t- let t =- addAliases- brancht- (`S.difference` S.map AliasBound (allConsumed dflow))- pure (NE.cons c' cs', t, retext)--checkCase ::- PatType ->- CaseBase NoInfo Name ->- TermTypeM (CaseBase Info VName, PatType, [VName])-checkCase mt (CasePat p e loc) =- bindingPat [] p (Ascribed mt) $ \p' -> do- e' <- checkExp e- (t, retext) <- unscopeType loc (patternMap p') =<< expTypeFully e'- pure (CasePat p' e' loc, t, retext)---- | An unmatched pattern. Used in in the generation of--- unmatched pattern warnings by the type checker.-data Unmatched p- = UnmatchedNum p [PatLit]- | UnmatchedBool p- | UnmatchedConstr p- | Unmatched p- deriving (Functor, Show)--instance Pretty (Unmatched (PatBase Info VName)) where- pretty um = case um of- (UnmatchedNum p nums) -> pretty' p <+> "where p is not one of" <+> pretty nums- (UnmatchedBool p) -> pretty' p- (UnmatchedConstr p) -> pretty' p- (Unmatched p) -> pretty' p- where- pretty' (PatAscription p t _) = pretty p <> ":" <+> pretty t- pretty' (PatParens p _) = parens $ pretty' p- pretty' (PatAttr _ p _) = parens $ pretty' p- pretty' (Id v _ _) = prettyName v- pretty' (TuplePat pats _) = parens $ commasep $ map pretty' pats- pretty' (RecordPat fs _) = braces $ commasep $ map ppField fs- where- ppField (name, t) = pretty (nameToString name) <> equals <> pretty' t- pretty' Wildcard {} = "_"- pretty' (PatLit e _ _) = pretty e- pretty' (PatConstr n _ ps _) = "#" <> pretty n <+> sep (map pretty' ps)--checkIdent :: IdentBase NoInfo Name -> TermTypeM Ident-checkIdent (Ident name _ loc) = do- (QualName _ name', vt) <- lookupVar loc (qualName name)- pure $ Ident name' (Info vt) loc--checkSlice :: UncheckedSlice -> TermTypeM Slice-checkSlice = mapM checkDimIndex- where- checkDimIndex (DimFix i) =- DimFix <$> (require "use as index" anySignedType =<< checkExp i)- checkDimIndex (DimSlice i j s) =- DimSlice <$> check i <*> check j <*> check s-- check =- maybe (pure Nothing) $- fmap Just . unifies "use as index" (Scalar $ Prim $ Signed Int64) <=< checkExp---- The number of dimensions affected by this slice (so the minimum--- rank of the array we are slicing).-sliceDims :: Slice -> Int-sliceDims = length--type Arg = (Exp, PatType, Occurrences, SrcLoc)--argExp :: Arg -> Exp-argExp (e, _, _, _) = e--argType :: Arg -> PatType-argType (_, t, _, _) = t--checkArg :: UncheckedExp -> TermTypeM Arg-checkArg arg = do- (arg', dflow) <- collectOccurrences $ checkExp arg- arg_t <- expType arg'- pure (arg', arg_t, dflow, srclocOf arg')--instantiateDimsInReturnType ::- SrcLoc ->- Maybe (QualName VName) ->- RetTypeBase Size als ->- TermTypeM (TypeBase Size als, [VName])-instantiateDimsInReturnType tloc fname =- instantiateEmptyArrayDims tloc $ Rigid $ RigidRet fname---- Some information about the function/operator we are trying to--- apply, and how many arguments it has previously accepted. Used for--- generating nicer type errors.-type ApplyOp = (Maybe (QualName VName), Int)---- | Extract all those names that are bound inside the type.-boundInsideType :: TypeBase Size as -> S.Set VName-boundInsideType (Array _ _ _ t) = boundInsideType (Scalar t)-boundInsideType (Scalar Prim {}) = mempty-boundInsideType (Scalar (TypeVar _ _ _ targs)) = foldMap f targs- where- f (TypeArgType t _) = boundInsideType t- f TypeArgDim {} = mempty-boundInsideType (Scalar (Record fs)) = foldMap boundInsideType fs-boundInsideType (Scalar (Sum cs)) = foldMap (foldMap boundInsideType) cs-boundInsideType (Scalar (Arrow _ pn _ t1 (RetType dims t2))) =- pn' <> boundInsideType t1 <> S.fromList dims <> boundInsideType t2- where- pn' = case pn of- Unnamed -> mempty- Named v -> S.singleton v---- Returns the sizes of the immediate type produced,--- the sizes of parameter types, and the sizes of return types.-dimUses :: StructType -> (Names, Names)-dimUses = flip execState mempty . traverseDims f- where- f bound _ (NamedSize v) | qualLeaf v `S.member` bound = pure ()- f _ PosImmediate (NamedSize v) = modify ((S.singleton (qualLeaf v), mempty) <>)- f _ PosParam (NamedSize v) = modify ((mempty, S.singleton (qualLeaf v)) <>)- f _ _ _ = pure ()--checkApply ::- SrcLoc ->- ApplyOp ->- PatType ->- Arg ->- TermTypeM (Diet, StructType, PatType, Maybe VName, [VName])-checkApply- loc- (fname, _)- (Scalar (Arrow as pname d1 tp1 tp2))- (argexp, argtype, dflow, argloc) =- onFailure (CheckingApply fname argexp tp1 (toStruct argtype)) $ do- expect (mkUsage argloc "use as function argument") (toStruct tp1) (toStruct argtype)-- -- Perform substitutions of instantiated variables in the types.- tp1' <- normTypeFully tp1- (tp2', ext) <- instantiateDimsInReturnType loc fname =<< normTypeFully tp2- argtype' <- normTypeFully argtype-- -- Check whether this would produce an impossible return type.- let (tp2_produced_dims, tp2_paramdims) = dimUses $ toStruct tp2'- problematic = S.fromList ext <> boundInsideType argtype'- when (any (`S.member` problematic) (tp2_paramdims `S.difference` tp2_produced_dims)) $ do- typeError loc mempty . withIndexLink "existential-param-ret" $- "Existential size would appear in function parameter of return type:"- </> indent 2 (pretty (RetType ext tp2'))- </> textwrap "This is usually because a higher-order function is used with functional arguments that return existential sizes or locally named sizes, which are then used as parameters of other function arguments."-- occur [observation as loc]-- checkOccurrences dflow-- case anyConsumption dflow of- Just c ->- let msg = "type of expression with consumption at " <> locText (location c)- in zeroOrderType (mkUsage argloc "potential consumption in expression") msg tp1- _ -> pure ()-- arg_consumed <- consumedByArg (locOf argloc) argtype' d1- checkIfConsumable loc $ mconcat arg_consumed- occur $ dflow `seqOccurrences` map (`consumption` argloc) arg_consumed-- -- Unification ignores uniqueness in higher-order arguments, so- -- we check for that here.- unless (toStructural argtype' `subtypeOf` setUniqueness (toStructural tp1') Nonunique) $- typeError loc mempty "Difference in whether argument is consumed."-- (argext, parsubst) <-- case pname of- Named pname'- | (Scalar (Prim (Signed Int64))) <- tp1' -> do- (d, argext) <- sizeFromArg fname argexp- pure- ( argext,- (`M.lookup` M.singleton pname' (SizeSubst d))- )- _ -> pure (Nothing, const Nothing)-- -- In case a function result is not immediately bound to a name,- -- we need to invent a name for it so we can track it during- -- aliasing (uniqueness-error54.fut, uniqueness-error55.fut,- -- uniqueness-error60.fut).- v <- newID "internal_app_result"- modify $ \s -> s {stateNames = M.insert v (NameAppRes fname loc) $ stateNames s}- let appres = S.singleton $ AliasFree v- let tp2'' = applySubst parsubst $ returnType appres tp2' d1 argtype'-- pure (d1, tp1', tp2'', argext, ext)-checkApply loc fname tfun@(Scalar TypeVar {}) arg = do- tv <- newTypeVar loc "b"- -- Change the uniqueness of the argument type because we never want- -- to infer that a function is consuming.- let argt_nonunique = toStruct (argType arg) `setUniqueness` Nonunique- unify (mkUsage loc "use as function") (toStruct tfun) $- Scalar (Arrow mempty Unnamed Observe argt_nonunique $ RetType [] tv)- tfun' <- normPatType tfun- checkApply loc fname tfun' arg-checkApply loc (fname, prev_applied) ftype (argexp, _, _, _) = do- let fname' = maybe "expression" (dquotes . pretty) fname-- typeError loc mempty $- if prev_applied == 0- then- "Cannot apply"- <+> fname'- <+> "as function, as it has type:"- </> indent 2 (pretty ftype)- else- "Cannot apply"- <+> fname'- <+> "to argument #" <> pretty (prev_applied + 1)- <+> dquotes (shorten $ group $ pretty argexp) <> ","- </> "as"- <+> fname'- <+> "only takes"- <+> pretty prev_applied- <+> arguments <> "."- where- arguments- | prev_applied == 1 = "argument"- | otherwise = "arguments"--aliasParts :: PatType -> [Aliasing]-aliasParts (Scalar (Record ts)) = foldMap aliasParts $ M.elems ts-aliasParts t = [aliases t]--consumedByArg :: Loc -> PatType -> Diet -> TermTypeM [Aliasing]-consumedByArg loc at Consume = do- let parts = aliasParts at- foldM_ check mempty parts- pure parts- where- check seen als- | any (`S.member` seen) als =- typeError loc mempty . withIndexLink "self-aliasing-arg" $- "Argument passed for consuming parameter is self-aliased."- | otherwise = pure $ als <> seen-consumedByArg _ _ _ = pure []---- | Type-check a single expression in isolation. This expression may--- turn out to be polymorphic, in which case the list of type--- parameters will be non-empty.-checkOneExp :: UncheckedExp -> TypeM ([TypeParam], Exp)-checkOneExp e = fmap fst . runTermTypeM $ do- e' <- checkExp e- let t = toStruct $ typeOf e'- (tparams, _, _) <-- letGeneralise (nameFromString "<exp>") (srclocOf e) [] [] t- fixOverloadedTypes $ typeVars t- e'' <- updateTypes e'- localChecks e''- causalityCheck e''- pure (tparams, e'')---- Verify that all sum type constructors and empty array literals have--- a size that is known (rigid or a type parameter). This is to--- ensure that we can actually determine their shape at run-time.-causalityCheck :: Exp -> TermTypeM ()-causalityCheck binding_body = do- constraints <- getConstraints-- let checkCausality what known t loc- | (d, dloc) : _ <-- mapMaybe (unknown constraints known) $- S.toList $- freeInType $- toStruct t =- Just $ lift $ causality what (locOf loc) d dloc t- | otherwise = Nothing-- checkParamCausality known p =- checkCausality (pretty p) known (patternType p) (locOf p)-- onExp ::- S.Set VName ->- Exp ->- StateT (S.Set VName) (Either TypeError) Exp-- onExp known (Var v (Info t) loc)- | Just bad <- checkCausality (dquotes (pretty v)) known t loc =- bad- onExp known (ProjectSection _ (Info t) loc)- | Just bad <- checkCausality "projection section" known t loc =- bad- onExp known (IndexSection _ (Info t) loc)- | Just bad <- checkCausality "projection section" known t loc =- bad- onExp known (OpSectionRight _ (Info t) _ _ _ loc)- | Just bad <- checkCausality "operator section" known t loc =- bad- onExp known (OpSectionLeft _ (Info t) _ _ _ loc)- | Just bad <- checkCausality "operator section" known t loc =- bad- onExp known (ArrayLit [] (Info t) loc)- | Just bad <- checkCausality "empty array" known t loc =- bad- onExp known (Hole (Info t) loc)- | Just bad <- checkCausality "hole" known t loc =- bad- onExp known (Lambda params _ _ _ _)- | bad : _ <- mapMaybe (checkParamCausality known) params =- bad- onExp known e@(AppExp (LetPat _ _ bindee_e body_e _) (Info res)) = do- sequencePoint known bindee_e body_e $ appResExt res- pure e- onExp known e@(AppExp (Apply f args _) (Info res)) = do- seqArgs known $ reverse $ NE.toList args- pure e- where- seqArgs known' [] = do- void $ onExp known' f- modify (S.fromList (appResExt res) <>)- seqArgs known' ((Info (_, p), x) : xs) = do- new_known <- lift $ execStateT (onExp known' x) mempty- void $ seqArgs (new_known <> known') xs- modify ((new_known <> S.fromList (maybeToList p)) <>)- onExp- known- e@(AppExp (BinOp (f, floc) ft (x, Info (_, xp)) (y, Info (_, yp)) _) (Info res)) = do- args_known <-- lift $- execStateT (sequencePoint known x y $ catMaybes [xp, yp]) mempty- void $ onExp (args_known <> known) (Var f ft floc)- modify ((args_known <> S.fromList (appResExt res)) <>)- pure e- onExp known e@(AppExp e' (Info res)) = do- recurse known e'- modify (<> S.fromList (appResExt res))- pure e- onExp known e = do- recurse known e- pure e-- recurse known = void . astMap mapper- where- mapper = identityMapper {mapOnExp = onExp known}-- sequencePoint known x y ext = do- new_known <- lift $ execStateT (onExp known x) mempty- void $ onExp (new_known <> known) y- modify ((new_known <> S.fromList ext) <>)-- either throwError (const $ pure ()) $- evalStateT (onExp mempty binding_body) mempty- where- unknown constraints known v = do- guard $ v `S.notMember` known- loc <- unknowable constraints v- pure (v, loc)-- unknowable constraints v =- case snd <$> M.lookup v constraints of- Just (UnknowableSize loc _) -> Just loc- _ -> Nothing-- causality what loc d dloc t =- Left . TypeError loc mempty . withIndexLink "causality-check" $- "Causality check: size"- </> dquotes (prettyName d)- </> "needed for type of"- <+> what <> colon- </> indent 2 (pretty t)- </> "But"- <+> dquotes (prettyName d)- <+> "is computed at"- </> pretty (locStrRel loc dloc) <> "."- </> ""- </> "Hint:"- <+> align- ( textwrap "Bind the expression producing"- <+> dquotes (prettyName d)- <+> "with 'let' beforehand."- )---- | Traverse the expression, emitting warnings and errors for various--- problems:------ * Unmatched cases.------ * If any of the literals overflow their inferred types. Note:--- currently unable to detect float underflow (such as 1e-400 -> 0)-localChecks :: Exp -> TermTypeM ()-localChecks = void . check- where- check e@(AppExp (Match _ cs loc) _) = do- let ps = fmap (\(CasePat p _ _) -> p) cs- case unmatched $ NE.toList ps of- [] -> recurse e- ps' ->- typeError loc mempty . withIndexLink "unmatched-cases" $- "Unmatched cases in match expression:"- </> indent 2 (stack (map pretty ps'))- check e@(IntLit x ty loc) =- e <$ case ty of- Info (Scalar (Prim t)) -> errorBounds (inBoundsI x t) x t loc- _ -> error "Inferred type of int literal is not a number"- check e@(FloatLit x ty loc) =- e <$ case ty of- Info (Scalar (Prim (FloatType t))) -> errorBounds (inBoundsF x t) x t loc- _ -> error "Inferred type of float literal is not a float"- check e@(Negate (IntLit x ty loc1) loc2) =- e <$ case ty of- Info (Scalar (Prim t)) -> errorBounds (inBoundsI (-x) t) (-x) t (loc1 <> loc2)- _ -> error "Inferred type of int literal is not a number"- check e@(AppExp (BinOp (QualName [] v, _) _ (_, Info (Array {}, _)) _ loc) _)- | baseName v == "==",- baseTag v <= maxIntrinsicTag = do- warn loc $- textwrap- "Comparing arrays with \"==\" is deprecated and will stop working in a future revision of the language."- recurse e- check e = recurse e- recurse = astMap identityMapper {mapOnExp = check}-- bitWidth ty = 8 * intByteSize ty :: Int-- inBoundsI x (Signed t) = x >= -2 ^ (bitWidth t - 1) && x < 2 ^ (bitWidth t - 1)- inBoundsI x (Unsigned t) = x >= 0 && x < 2 ^ bitWidth t- inBoundsI x (FloatType Float16) = not $ isInfinite (fromIntegral x :: Half)- inBoundsI x (FloatType Float32) = not $ isInfinite (fromIntegral x :: Float)- inBoundsI x (FloatType Float64) = not $ isInfinite (fromIntegral x :: Double)- inBoundsI _ Bool = error "Inferred type of int literal is not a number"- inBoundsF x Float16 = not $ isInfinite (realToFrac x :: Float)- inBoundsF x Float32 = not $ isInfinite (realToFrac x :: Float)- inBoundsF x Float64 = not $ isInfinite x-- errorBounds inBounds x ty loc =- unless inBounds $- typeError loc mempty . withIndexLink "literal-out-of-bounds" $- "Literal "- <> pretty x- <> " out of bounds for inferred type "- <> pretty ty- <> "."---- | Type-check a top-level (or module-level) function definition.--- Despite the name, this is also used for checking constant--- definitions, by treating them as 0-ary functions.-checkFunDef ::- ( Name,- Maybe UncheckedTypeExp,- [UncheckedTypeParam],- [UncheckedPat],- UncheckedExp,- SrcLoc- ) ->- TypeM- ( VName,- [TypeParam],- [Pat],- Maybe (TypeExp Info VName),- StructRetType,- Exp- )-checkFunDef (fname, maybe_retdecl, tparams, params, body, loc) =- fmap fst . runTermTypeM $ do- (tparams', params', maybe_retdecl', RetType dims rettype', body') <-- checkBinding (fname, maybe_retdecl, tparams, params, body, loc)-- -- Since this is a top-level function, we also resolve overloaded- -- types, using either defaults or complaining about ambiguities.- fixOverloadedTypes $- typeVars rettype' <> foldMap (typeVars . patternType) params'-- -- Then replace all inferred types in the body and parameters.- body'' <- updateTypes body'- params'' <- updateTypes params'- maybe_retdecl'' <- traverse updateTypes maybe_retdecl'- rettype'' <- normTypeFully rettype'-- -- Check if the function body can actually be evaluated.- causalityCheck body''-- -- Check for various problems.- localChecks body''-- bindSpaced [(Term, fname)] $ do- fname' <- checkName Term fname loc- when (nameToString fname `elem` doNotShadow) $- typeError loc mempty . withIndexLink "may-not-be-redefined" $- "The" <+> prettyName fname <+> "operator may not be redefined."-- pure (fname', tparams', params'', maybe_retdecl'', RetType dims rettype'', body'')---- | This is "fixing" as in "setting them", not "correcting them". We--- only make very conservative fixing.-fixOverloadedTypes :: Names -> TermTypeM ()-fixOverloadedTypes tyvars_at_toplevel =- getConstraints >>= mapM_ fixOverloaded . M.toList . M.map snd- where- fixOverloaded (v, Overloaded ots usage)- | Signed Int32 `elem` ots = do- unify usage (Scalar (TypeVar () Nonunique (qualName v) [])) $- Scalar $- Prim $- Signed Int32- when (v `S.member` tyvars_at_toplevel) $- warn usage "Defaulting ambiguous type to i32."- | FloatType Float64 `elem` ots = do- unify usage (Scalar (TypeVar () Nonunique (qualName v) [])) $- Scalar $- Prim $- FloatType Float64- when (v `S.member` tyvars_at_toplevel) $- warn usage "Defaulting ambiguous type to f64."- | otherwise =- typeError usage mempty . withIndexLink "ambiguous-type" $- "Type is ambiguous (could be one of"- <+> commasep (map pretty ots) <> ")."- </> "Add a type annotation to disambiguate the type."- fixOverloaded (v, NoConstraint _ usage) = do- -- See #1552.- unify usage (Scalar (TypeVar () Nonunique (qualName v) [])) $- Scalar $- tupleRecord []- when (v `S.member` tyvars_at_toplevel) $- warn usage "Defaulting ambiguous type to ()."- fixOverloaded (_, Equality usage) =- typeError usage mempty . withIndexLink "ambiguous-type" $- "Type is ambiguous (must be equality type)."- </> "Add a type annotation to disambiguate the type."- fixOverloaded (_, HasFields _ fs usage) =- typeError usage mempty . withIndexLink "ambiguous-type" $- "Type is ambiguous. Must be record with fields:"- </> indent 2 (stack $ map field $ M.toList fs)- </> "Add a type annotation to disambiguate the type."- where- field (l, t) = pretty l <> colon <+> align (pretty t)- fixOverloaded (_, HasConstrs _ cs usage) =- typeError usage mempty . withIndexLink "ambiguous-type" $- "Type is ambiguous (must be a sum type with constructors:"- <+> pretty (Sum cs) <> ")."- </> "Add a type annotation to disambiguate the type."- fixOverloaded (v, Size Nothing (Usage Nothing loc)) =- typeError loc mempty . withIndexLink "ambiguous-size" $- "Ambiguous size" <+> dquotes (prettyName v) <> "."- fixOverloaded (v, Size Nothing (Usage (Just u) loc)) =- typeError loc mempty . withIndexLink "ambiguous-size" $- "Ambiguous size" <+> dquotes (prettyName v) <+> "arising from" <+> pretty u <> "."- fixOverloaded _ = pure ()--hiddenParamNames :: [Pat] -> Names-hiddenParamNames params = hidden- where- param_all_names = mconcat $ map patNames params- named (Named x, _, _) = Just x- named (Unnamed, _, _) = Nothing- param_names =- S.fromList $ mapMaybe (named . patternParam) params- hidden = param_all_names `S.difference` param_names--inferredReturnType :: SrcLoc -> [Pat] -> PatType -> TermTypeM StructType-inferredReturnType loc params t = do- -- The inferred type may refer to names that are bound by the- -- parameter patterns, but which will not be visible in the type.- -- These we must turn into fresh type variables, which will be- -- existential in the return type.- fmap (toStruct . fst) $- unscopeType loc hidden_params $- inferReturnUniqueness params t- where- hidden_params = M.filterWithKey (const . (`S.member` hidden)) $ foldMap patternMap params- hidden = hiddenParamNames params--checkReturnAlias :: SrcLoc -> TypeBase () () -> [Pat] -> PatType -> TermTypeM ()-checkReturnAlias loc rettp params =- foldM_ (checkReturnAlias' params) S.empty . returnAliasing rettp- where- checkReturnAlias' params' seen (Unique, names)- | any (`S.member` S.map snd seen) $ S.toList names =- uniqueReturnAliased loc- | otherwise = do- notAliasingParam params' names- pure $ seen `S.union` tag Unique names- checkReturnAlias' _ seen (Nonunique, names)- | any (`S.member` seen) $ S.toList $ tag Unique names =- uniqueReturnAliased loc- | otherwise = pure $ seen `S.union` tag Nonunique names-- notAliasingParam params' names =- forM_ params' $ \p ->- let consumedNonunique p' =- not (consumableParamType $ unInfo $ identType p') && (identName p' `S.member` names)- in case find consumedNonunique $ S.toList $ patIdents p of- Just p' ->- returnAliased (baseName $ identName p') loc- Nothing ->- pure ()-- tag u = S.map (u,)-- returnAliasing (Scalar (Record ets1)) (Scalar (Record ets2)) =- concat $ M.elems $ M.intersectionWith returnAliasing ets1 ets2- returnAliasing expected got =- [(uniqueness expected, S.map aliasVar $ aliases got)]-- consumableParamType (Array _ u _ _) = u == Unique- consumableParamType (Scalar Prim {}) = True- consumableParamType (Scalar (TypeVar _ u _ _)) = u == Unique- consumableParamType (Scalar (Record fs)) = all consumableParamType fs- consumableParamType (Scalar (Sum fs)) = all (all consumableParamType) fs- consumableParamType (Scalar Arrow {}) = False--checkBinding ::- ( Name,- Maybe UncheckedTypeExp,- [UncheckedTypeParam],- [UncheckedPat],- UncheckedExp,- SrcLoc- ) ->- TermTypeM- ( [TypeParam],- [Pat],- Maybe (TypeExp Info VName),- StructRetType,- Exp- )-checkBinding (fname, maybe_retdecl, tparams, params, body, loc) =- noUnique . incLevel . bindingParams tparams params $ \tparams' params' -> do- maybe_retdecl' <- traverse checkTypeExpNonrigid maybe_retdecl-- body' <-- checkFunBody- params'- body- ((\(_, x, _) -> x) <$> maybe_retdecl')- (maybe loc srclocOf maybe_retdecl)-- params'' <- mapM updateTypes params'- body_t <- expTypeFully body'-- (maybe_retdecl'', rettype) <- case maybe_retdecl' of- Just (retdecl', ret, _) -> do- let rettype_structural = toStructural ret- checkReturnAlias loc rettype_structural params'' body_t-- when (null params) $ nothingMustBeUnique loc rettype_structural-- ret' <- normTypeFully ret-- pure (Just retdecl', ret')- Nothing- | null params ->- pure (Nothing, toStruct body_t)- | otherwise -> do- body_t' <- inferredReturnType loc params'' body_t- pure (Nothing, body_t')-- verifyFunctionParams (Just fname) params''-- (tparams'', params''', rettype') <-- letGeneralise fname loc tparams' params'' rettype-- checkGlobalAliases params'' body_t loc-- pure (tparams'', params''', maybe_retdecl'', rettype', body')---- | Extract all the shape names that occur in positive position--- (roughly, left side of an arrow) in a given type.-sizeNamesPos :: TypeBase Size als -> S.Set VName-sizeNamesPos (Scalar (Arrow _ _ _ t1 (RetType _ t2))) = onParam t1 <> sizeNamesPos t2- where- onParam :: TypeBase Size als -> S.Set VName- onParam (Scalar Arrow {}) = mempty- onParam (Scalar (Record fs)) = mconcat $ map onParam $ M.elems fs- onParam (Scalar (TypeVar _ _ _ targs)) = mconcat $ map onTypeArg targs- onParam t = freeInType t- onTypeArg (TypeArgDim (NamedSize d) _) = S.singleton $ qualLeaf d- onTypeArg (TypeArgDim _ _) = mempty- onTypeArg (TypeArgType t _) = onParam t-sizeNamesPos _ = mempty--checkGlobalAliases :: [Pat] -> PatType -> SrcLoc -> TermTypeM ()-checkGlobalAliases params body_t loc = do- vtable <- asks $ scopeVtable . termScope- let isGlobal v = case v `M.lookup` vtable of- Just (BoundV Global _ _) -> True- _ -> False- let als =- filter isGlobal . S.toList $- boundArrayAliases body_t `S.difference` foldMap patNames params- unless (null params) $ case als of- v : _ ->- typeError loc mempty . withIndexLink "alias-free-variable" $- "Function result aliases the free variable "- <> dquotes (prettyName v)- <> "."- </> "Use"- <+> dquotes "copy"- <+> "to break the aliasing."- _ ->- pure ()--inferReturnUniqueness :: [Pat] -> PatType -> PatType-inferReturnUniqueness params t =- let forbidden = aliasesMultipleTimes t- uniques = uniqueParamNames params- delve (Scalar (Record fs)) =- Scalar $ Record $ M.map delve fs- delve (Scalar (Sum cs)) =- Scalar $ Sum $ M.map (map delve) cs- delve t'- | all (`S.member` uniques) (boundArrayAliases t'),- not $ any ((`S.member` forbidden) . aliasVar) (aliases t') =- t' `setUniqueness` Unique- | otherwise =- t' `setUniqueness` Nonunique- in delve t---- An alias inhibits uniqueness if it is used in disjoint values.-aliasesMultipleTimes :: PatType -> Names-aliasesMultipleTimes = S.fromList . map fst . filter ((> 1) . snd) . M.toList . delve- where- delve (Scalar (Record fs)) =- foldl' (M.unionWith (+)) mempty $ map delve $ M.elems fs- delve t =- M.fromList $ zip (map aliasVar $ S.toList (aliases t)) $ repeat (1 :: Int)--uniqueParamNames :: [Pat] -> Names-uniqueParamNames =- S.map identName- . S.filter (unique . unInfo . identType)- . foldMap patIdents--boundArrayAliases :: PatType -> S.Set VName-boundArrayAliases (Array als _ _ _) = boundAliases als-boundArrayAliases (Scalar Prim {}) = mempty-boundArrayAliases (Scalar (Record fs)) = foldMap boundArrayAliases fs-boundArrayAliases (Scalar (TypeVar als _ _ _)) = boundAliases als-boundArrayAliases (Scalar Arrow {}) = mempty-boundArrayAliases (Scalar (Sum fs)) =- mconcat $ concatMap (map boundArrayAliases) $ M.elems fs--nothingMustBeUnique :: SrcLoc -> TypeBase () () -> TermTypeM ()-nothingMustBeUnique loc = check- where- check (Array _ Unique _ _) = bad- check (Scalar (TypeVar _ Unique _ _)) = bad- check (Scalar (Record fs)) = mapM_ check fs- check (Scalar (Sum fs)) = mapM_ (mapM_ check) fs- check _ = pure ()- bad = typeError loc mempty "A top-level constant cannot have a unique type."---- | Verify certain restrictions on function parameters, and bail out--- on dubious constructions.------ These restrictions apply to all functions (anonymous or otherwise).--- Top-level functions have further restrictions that are checked--- during let-generalisation.-verifyFunctionParams :: Maybe Name -> [Pat] -> TermTypeM ()-verifyFunctionParams fname params =- onFailure (CheckingParams fname) $- verifyParams (foldMap patNames params) =<< mapM updateTypes params- where- verifyParams forbidden (p : ps)- | d : _ <- S.toList $ freeInPat p `S.intersection` forbidden =- typeError p mempty . withIndexLink "inaccessible-size" $- "Parameter"- <+> dquotes (pretty p)- </> "refers to size"- <+> dquotes (prettyName d)- <> comma- </> textwrap "which will not be accessible to the caller"- <> comma- </> textwrap "possibly because it is nested in a tuple or record."- </> textwrap "Consider ascribing an explicit type that does not reference "- <> dquotes (prettyName d)- <> "."- | otherwise = verifyParams forbidden' ps- where- forbidden' =- case patternParam p of- (Named v, _, _) -> forbidden `S.difference` S.singleton v- _ -> forbidden- verifyParams _ [] = pure ()---- | Move existentials down to the level where they are actually used--- (i.e. have their "witnesses"). E.g. changes------ @--- ?[n].bool -> [n]bool--- @------ to------ @--- bool -> ?[n].[n]bool--- @-injectExt :: [VName] -> StructType -> StructRetType-injectExt [] ret = RetType [] ret-injectExt ext ret = RetType ext_here $ deeper ret- where- (immediate, _) = dimUses ret- (ext_here, ext_there) = partition (`S.member` immediate) ext- deeper (Scalar (Prim t)) = Scalar $ Prim t- deeper (Scalar (Record fs)) = Scalar $ Record $ M.map deeper fs- deeper (Scalar (Sum cs)) = Scalar $ Sum $ M.map (map deeper) cs- deeper (Scalar (Arrow als p d1 t1 (RetType t2_ext t2))) =- Scalar $ Arrow als p d1 t1 $ injectExt (ext_there <> t2_ext) t2- deeper (Scalar (TypeVar as u tn targs)) =- Scalar $ TypeVar as u tn $ map deeperArg targs- deeper t@Array {} = t-- deeperArg (TypeArgType t loc) = TypeArgType (deeper t) loc- deeperArg (TypeArgDim d loc) = TypeArgDim d loc---- | Find all type variables in the given type that are covered by the--- constraints, and produce type parameters that close over them.------ The passed-in list of type parameters is always prepended to the--- produced list of type parameters.-closeOverTypes ::- Name ->- SrcLoc ->- [TypeParam] ->- [StructType] ->- StructType ->- Constraints ->- TermTypeM ([TypeParam], StructRetType)-closeOverTypes defname defloc tparams paramts ret substs = do- (more_tparams, retext) <-- partitionEithers . catMaybes- <$> mapM closeOver (M.toList $ M.map snd to_close_over)- let mkExt v =- case M.lookup v substs of- Just (_, UnknowableSize {}) -> Just v- _ -> Nothing- pure- ( tparams ++ more_tparams,- injectExt (retext ++ mapMaybe mkExt (S.toList $ freeInType ret)) ret- )- where- -- Diet does not matter here.- t = foldFunType (zip (repeat Observe) paramts) $ RetType [] ret- to_close_over = M.filterWithKey (\k _ -> k `S.member` visible) substs- visible = typeVars t <> freeInType t-- (produced_sizes, param_sizes) = dimUses t-- -- Avoid duplicate type parameters.- closeOver (k, _)- | k `elem` map typeParamName tparams =- pure Nothing- closeOver (k, NoConstraint l usage) =- pure $ Just $ Left $ TypeParamType l k $ srclocOf usage- closeOver (k, ParamType l loc) =- pure $ Just $ Left $ TypeParamType l k loc- closeOver (k, Size Nothing usage) =- pure $ Just $ Left $ TypeParamDim k $ srclocOf usage- closeOver (k, UnknowableSize _ _)- | k `S.member` param_sizes,- k `S.notMember` produced_sizes = do- notes <- dimNotes defloc $ NamedSize $ qualName k- typeError defloc notes . withIndexLink "unknowable-param-def" $- "Unknowable size"- <+> dquotes (prettyName k)- <+> "in parameter of"- <+> dquotes (prettyName defname)- <> ", which is inferred as:"- </> indent 2 (pretty t)- | k `S.member` produced_sizes =- pure $ Just $ Right k- closeOver (_, _) =- pure Nothing--letGeneralise ::- Name ->- SrcLoc ->- [TypeParam] ->- [Pat] ->- StructType ->- TermTypeM ([TypeParam], [Pat], StructRetType)-letGeneralise defname defloc tparams params rettype =- onFailure (CheckingLetGeneralise defname) $ do- now_substs <- getConstraints-- -- Candidates for let-generalisation are those type variables that- --- -- (1) were not known before we checked this function, and- --- -- (2) are not used in the (new) definition of any type variables- -- known before we checked this function.- --- -- (3) are not referenced from an overloaded type (for example,- -- are the element types of an incompletely resolved record type).- -- This is a bit more restrictive than I'd like, and SML for- -- example does not have this restriction.- --- -- Criteria (1) and (2) is implemented by looking at the binding- -- level of the type variables.- let keep_type_vars = overloadedTypeVars now_substs-- cur_lvl <- curLevel- let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl- new_substs = M.filterWithKey candidate now_substs-- (tparams', RetType ret_dims rettype') <-- closeOverTypes- defname- defloc- tparams- (map patternStructType params)- rettype- new_substs-- rettype'' <- updateTypes rettype'-- let used_sizes =- foldMap freeInType $ rettype'' : map patternStructType params- case filter ((`S.notMember` used_sizes) . typeParamName) $- filter isSizeParam tparams' of- [] -> pure ()- tp : _ -> unusedSize $ SizeBinder (typeParamName tp) (srclocOf tp)-- -- We keep those type variables that were not closed over by- -- let-generalisation.- modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` map typeParamName tparams'-- pure (tparams', params, RetType ret_dims rettype'')--checkFunBody ::- [Pat] ->- UncheckedExp ->- Maybe StructType ->- SrcLoc ->- TermTypeM Exp-checkFunBody params body maybe_rettype loc = do- body' <- noSizeEscape $ checkExp body-- -- Unify body return type with return annotation, if one exists.- case maybe_rettype of- Just rettype -> do- body_t <- expTypeFully body'- -- We need to turn any sizes provided by "hidden" parameter- -- names into existential sizes instead.- let hidden = hiddenParamNames params- (body_t', _) <-- unscopeType- loc- ( M.filterWithKey (const . (`S.member` hidden)) $- foldMap patternMap params- )- body_t-- let usage = mkUsage (srclocOf body) "return type annotation"- onFailure (CheckingReturn rettype (toStruct body_t')) $- expect usage rettype $- toStruct body_t'- Nothing -> pure ()-- pure body'--arrayOfM ::- (Pretty (Shape dim), Monoid as) =>- SrcLoc ->- TypeBase dim as ->- Shape dim ->- Uniqueness ->- TermTypeM (TypeBase dim as)-arrayOfM loc t shape u = do- arrayElemType (mkUsage loc "use as array element") "type used in array" t- pure $ arrayOf u shape t+ checkSizeExp,+ checkFunDef,+ )+where++import Control.Monad+import Control.Monad.Except+import Control.Monad.Identity+import Control.Monad.Reader+import Control.Monad.State.Strict+import Data.Bifunctor+import Data.Bitraversable+import Data.Char (isAscii)+import Data.Either+import Data.List (delete, find, genericLength, partition)+import Data.List.NonEmpty qualified as NE+import Data.Map.Strict qualified as M+import Data.Maybe+import Data.Set qualified as S+import Futhark.Util (mapAccumLM, topologicalSort)+import Futhark.Util.Pretty hiding (space)+import Language.Futhark+import Language.Futhark.Primitive (intByteSize)+import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Consumption qualified as Consumption+import Language.Futhark.TypeChecker.Match+import Language.Futhark.TypeChecker.Monad hiding (BoundV)+import Language.Futhark.TypeChecker.Terms.DoLoop+import Language.Futhark.TypeChecker.Terms.Monad+import Language.Futhark.TypeChecker.Terms.Pat+import Language.Futhark.TypeChecker.Types+import Language.Futhark.TypeChecker.Unify+import Prelude hiding (mod)++hasBinding :: Exp -> Bool+hasBinding Lambda {} = True+hasBinding (AppExp LetPat {} _) = True+hasBinding (AppExp LetFun {} _) = True+hasBinding (AppExp DoLoop {} _) = True+hasBinding (AppExp LetWith {} _) = True+hasBinding (AppExp Match {} _) = True+hasBinding e = isNothing $ astMap m e+ where+ m =+ identityMapper {mapOnExp = \e' -> if hasBinding e' then Nothing else Just e'}++overloadedTypeVars :: Constraints -> Names+overloadedTypeVars = mconcat . map f . M.elems+ where+ f (_, HasFields _ fs _) = mconcat $ map typeVars $ M.elems fs+ f _ = mempty++--- Basic checking++-- | Determine if the two types are identical, ignoring uniqueness.+-- Mismatched dimensions are turned into fresh rigid type variables.+-- Causes a 'TypeError' if they fail to match, and otherwise returns+-- one of them.+unifyBranchTypes :: SrcLoc -> StructType -> StructType -> TermTypeM (StructType, [VName])+unifyBranchTypes loc t1 t2 =+ onFailure (CheckingBranches t1 t2) $+ unifyMostCommon (mkUsage loc "unification of branch results") t1 t2++unifyBranches :: SrcLoc -> Exp -> Exp -> TermTypeM (StructType, [VName])+unifyBranches loc e1 e2 = do+ e1_t <- expTypeFully e1+ e2_t <- expTypeFully e2+ unifyBranchTypes loc e1_t e2_t++sliceShape ::+ Maybe (SrcLoc, Rigidity) ->+ [DimIndex] ->+ TypeBase Size as ->+ TermTypeM (TypeBase Size as, [VName])+sliceShape r slice t@(Array u (Shape orig_dims) et) =+ runStateT (setDims <$> adjustDims slice orig_dims) []+ where+ setDims [] = stripArray (length orig_dims) t+ setDims dims' = Array u (Shape dims') et++ -- If the result is supposed to be a nonrigid size variable, then+ -- don't bother trying to create non-existential sizes. This is+ -- necessary to make programs type-check without too much+ -- ceremony; see e.g. tests/inplace5.fut.+ isRigid Rigid {} = True+ isRigid _ = False+ refine_sizes = maybe False (isRigid . snd) r++ sliceSize orig_d i j stride =+ case r of+ Just (loc, Rigid _) -> do+ (d, ext) <-+ lift . extSize loc $+ SourceSlice orig_d' (bareExp <$> i) (bareExp <$> j) (bareExp <$> stride)+ modify (maybeToList ext ++)+ pure d+ Just (loc, Nonrigid) ->+ lift $+ flip sizeFromName loc . qualName+ <$> newFlexibleDim (mkUsage loc "size of slice") "slice_dim"+ Nothing -> do+ v <- lift $ newID "slice_anydim"+ modify (v :)+ pure $ sizeFromName (qualName v) mempty+ where+ -- The original size does not matter if the slice is fully specified.+ orig_d'+ | isJust i, isJust j = Nothing+ | otherwise = Just orig_d++ warnIfBinding binds d i j stride size =+ if binds+ then do+ lift . warn (srclocOf size) $+ withIndexLink+ "size-expression-bind"+ "Size expression with binding is replaced by unknown size."+ (:) <$> sliceSize d i j stride+ else pure (size :)++ adjustDims (DimFix {} : idxes') (_ : dims) =+ adjustDims idxes' dims+ -- Pat match some known slices to be non-existential.+ adjustDims (DimSlice i j stride : idxes') (d : dims)+ | refine_sizes,+ maybe True ((== Just 0) . isInt64) i,+ maybe True ((== Just 1) . isInt64) stride = do+ let binds = maybe False hasBinding j+ warnIfBinding binds d i j stride (fromMaybe d j)+ <*> adjustDims idxes' dims+ adjustDims ((DimSlice i j stride) : idxes') (d : dims)+ | refine_sizes,+ Just i' <- i, -- if i ~ 0, previous case+ maybe True ((== Just 1) . isInt64) stride = do+ let j' = fromMaybe d j+ binds = hasBinding j' || hasBinding i'+ warnIfBinding binds d i j stride (sizeMinus j' i')+ <*> adjustDims idxes' dims+ -- stride == -1+ adjustDims ((DimSlice Nothing Nothing stride) : idxes') (d : dims)+ | refine_sizes,+ maybe True ((== Just (-1)) . isInt64) stride =+ (d :) <$> adjustDims idxes' dims+ adjustDims ((DimSlice (Just i) (Just j) stride) : idxes') (d : dims)+ | refine_sizes,+ maybe True ((== Just (-1)) . isInt64) stride = do+ let binds = hasBinding i || hasBinding j+ warnIfBinding binds d (Just i) (Just j) stride (sizeMinus i j)+ <*> adjustDims idxes' dims+ -- existential+ adjustDims ((DimSlice i j stride) : idxes') (d : dims) =+ (:) <$> sliceSize d i j stride <*> adjustDims idxes' dims+ adjustDims _ dims =+ pure dims++ sizeMinus j i =+ AppExp+ ( BinOp+ (qualName (intrinsicVar "-"), mempty)+ sizeBinOpInfo+ (j, Info Nothing)+ (i, Info Nothing)+ mempty+ )+ $ Info+ $ AppRes i64 []+ i64 = Scalar $ Prim $ Signed Int64+ sizeBinOpInfo = Info $ foldFunType [i64, i64] $ RetType [] i64+sliceShape _ _ t = pure (t, [])++--- Main checkers++checkAscript ::+ SrcLoc ->+ UncheckedTypeExp ->+ UncheckedExp ->+ TermTypeM (TypeExp Info VName, Exp)+checkAscript loc te e = do+ (te', decl_t, _) <- checkTypeExpNonrigid te+ e' <- checkExp e+ e_t <- expTypeFully e'++ onFailure (CheckingAscription (toStruct decl_t) e_t) $+ unify (mkUsage loc "type ascription") (toStruct decl_t) e_t++ pure (te', e')++checkCoerce ::+ SrcLoc ->+ UncheckedTypeExp ->+ UncheckedExp ->+ TermTypeM (TypeExp Info VName, StructType, Exp)+checkCoerce loc te e = do+ (te', te_t, ext) <- checkTypeExpNonrigid te+ e' <- checkExp e+ e_t <- expTypeFully e'++ te_t_nonrigid <- makeNonExtFresh ext $ toStruct te_t++ onFailure (CheckingAscription (toStruct te_t) e_t) $+ unify (mkUsage loc "size coercion") e_t te_t_nonrigid++ -- If the type expression had any anonymous dimensions, these will+ -- now be in 'ext'. Those we keep nonrigid and unify with e_t.+ -- This ensures that 'x :> [1][]i32' does not make the second+ -- dimension unknown. Use of matchDims is sensible because the+ -- structure of e_t' will be fully known due to the unification, and+ -- te_t because type expressions are complete.+ pure (te', toStruct te_t, e')+ where+ makeNonExtFresh ext = bitraverse onDim pure+ where+ onDim d@(Var v _ _)+ | qualLeaf v `elem` ext = pure d+ onDim d = do+ v <- newTypeName "coerce"+ constrain v . Size Nothing $+ mkUsage+ loc+ "a size coercion where the underlying expression size cannot be determined"+ pure $ sizeFromName (qualName v) (srclocOf d)++sameExp :: Exp -> Exp -> Bool+sameExp e1 e2+ | Just es <- similarExps e1 e2 =+ all (uncurry sameExp) es+ | otherwise = False++-- All non-trivial subexpressions (as by stripExp) of some expression,+-- not including the expression itself.+subExps :: Exp -> [Exp]+subExps e+ | Just e' <- stripExp e = subExps e'+ | otherwise = astMap mapper e `execState` mempty+ where+ mapOnExp e'+ | Just e'' <- stripExp e' = mapOnExp e''+ | otherwise = do+ modify (e' :)+ astMap mapper e'+ mapper = identityMapper {mapOnExp}++-- Expressions witnessed by type, topologically sorted.+topWit :: TypeBase Exp u -> [Exp]+topWit = topologicalSort depends . witnessedExps+ where+ witnessedExps t = execState (traverseDims onDim t) mempty+ where+ onDim _ PosImmediate e = modify (e :)+ onDim _ _ _ = pure ()+ depends a b = any (sameExp b) $ subExps a++sizeFree ::+ SrcLoc ->+ (Exp -> Maybe VName) ->+ TypeBase Size u ->+ TermTypeM (TypeBase Size u, [VName])+sizeFree tloc expKiller orig_t = do+ runReaderT (toBeReplaced orig_t $ onType orig_t) mempty `runStateT` mempty+ where+ lookReplacement e repl = snd <$> find (sameExp e . fst) repl+ expReplace mapping e+ | Just e' <- lookReplacement e mapping = e'+ | otherwise = runIdentity $ astMap mapper e+ where+ mapper = identityMapper {mapOnExp = pure . expReplace mapping}++ replacing e = do+ e' <- asks (`expReplace` e)+ case expKiller e' of+ Nothing -> pure e'+ Just cause -> do+ vn <- lift $ lift $ newRigidDim tloc (RigidOutOfScope (srclocOf e) cause) "d"+ modify (vn :)+ pure $ sizeFromName (qualName vn) (srclocOf e)++ toBeReplaced t m' = foldl f m' $ topWit t+ where+ f m e = do+ e' <- replacing e+ local ((e, e') :) m++ onScalar (Record fs) =+ Record <$> traverse onType fs+ onScalar (Sum cs) =+ Sum <$> (traverse . traverse) onType cs+ onScalar (Arrow as pn d argT (RetType dims retT)) = do+ argT' <- onType argT+ old_bound <- get+ retT' <- toBeReplaced retT $ onType retT+ rl <- state $ partition (`notElem` old_bound)+ let dims' = dims <> rl+ pure $ Arrow as pn d argT' (RetType dims' retT')+ onScalar (TypeVar u v args) =+ TypeVar u v <$> mapM onTypeArg args+ where+ onTypeArg (TypeArgDim d) = TypeArgDim <$> replacing d+ onTypeArg (TypeArgType ty) = TypeArgType <$> onType ty+ onScalar (Prim pt) = pure $ Prim pt++ onType ::+ TypeBase Size u ->+ ReaderT [(Exp, Exp)] (StateT [VName] TermTypeM) (TypeBase Size u)+ onType (Array u shape scalar) =+ Array u <$> traverse replacing shape <*> onScalar scalar+ onType (Scalar ty) =+ Scalar <$> onScalar ty++-- Used to remove unknown sizes from function body types before we+-- perform let-generalisation. This is because if a function is+-- inferred to return something of type '[x+y]t' where 'x' or 'y' are+-- unknown, we want to turn that into '[z]t', where ''z' is a fresh+-- unknown, which is then by let-generalisation turned into+-- '?[z].[z]t'.+unscopeUnknown ::+ TypeBase Size u ->+ TermTypeM (TypeBase Size u)+unscopeUnknown t = do+ constraints <- getConstraints+ -- These sizes will be immediately turned into existentials, so we+ -- do not need to care about their location.+ fst <$> sizeFree mempty (expKiller constraints) t+ where+ expKiller _ Var {} = Nothing+ expKiller constraints e =+ S.lookupMin $ S.filter (isUnknown constraints) $ (`S.difference` witnesses) $ fvVars $ freeInExp e+ isUnknown constraints vn+ | Just UnknownSize {} <- snd <$> M.lookup vn constraints = True+ isUnknown _ _ = False+ (witnesses, _) = determineSizeWitnesses $ toStruct t++unscopeType ::+ SrcLoc ->+ [VName] ->+ TypeBase Size as ->+ TermTypeM (TypeBase Size as, [VName])+unscopeType tloc unscoped =+ sizeFree tloc $ find (`elem` unscoped) . fvVars . freeInExp++checkExp :: UncheckedExp -> TermTypeM Exp+checkExp (Literal val loc) =+ pure $ Literal val loc+checkExp (Hole _ loc) = do+ t <- newTypeVar loc "t"+ pure $ Hole (Info t) loc+checkExp (StringLit vs loc) =+ pure $ StringLit vs loc+checkExp (IntLit val NoInfo loc) = do+ t <- newTypeVar loc "t"+ mustBeOneOf anyNumberType (mkUsage loc "integer literal") t+ pure $ IntLit val (Info t) loc+checkExp (FloatLit val NoInfo loc) = do+ t <- newTypeVar loc "t"+ mustBeOneOf anyFloatType (mkUsage loc "float literal") t+ pure $ FloatLit val (Info t) loc+checkExp (TupLit es loc) =+ TupLit <$> mapM checkExp es <*> pure loc+checkExp (RecordLit fs loc) = do+ fs' <- evalStateT (mapM checkField fs) mempty++ pure $ RecordLit fs' loc+ where+ checkField (RecordFieldExplicit f e rloc) = do+ errIfAlreadySet f rloc+ modify $ M.insert f rloc+ RecordFieldExplicit f <$> lift (checkExp e) <*> pure rloc+ checkField (RecordFieldImplicit name NoInfo rloc) = do+ errIfAlreadySet name rloc+ (QualName _ name', t) <- lift $ lookupVar rloc $ qualName name+ modify $ M.insert name rloc+ pure $ RecordFieldImplicit name' (Info t) rloc++ errIfAlreadySet f rloc = do+ maybe_sloc <- gets $ M.lookup f+ case maybe_sloc of+ Just sloc ->+ lift . typeError rloc mempty $+ "Field"+ <+> dquotes (pretty f)+ <+> "previously defined at"+ <+> pretty (locStrRel rloc sloc) <> "."+ Nothing -> pure ()+checkExp (ArrayLit all_es _ loc) =+ -- Construct the result type and unify all elements with it. We+ -- only create a type variable for empty arrays; otherwise we use+ -- the type of the first element. This significantly cuts down on+ -- the number of type variables generated for pathologically large+ -- multidimensional array literals.+ case all_es of+ [] -> do+ et <- newTypeVar loc "t"+ t <- arrayOfM loc et (Shape [sizeFromInteger 0 mempty])+ pure $ ArrayLit [] (Info t) loc+ e : es -> do+ e' <- checkExp e+ et <- expType e'+ es' <- mapM (unifies "type of first array element" et <=< checkExp) es+ et' <- normTypeFully et+ t <- arrayOfM loc et' (Shape [sizeFromInteger (genericLength all_es) mempty])+ pure $ ArrayLit (e' : es') (Info t) loc+checkExp (AppExp (Range start maybe_step end loc) _) = do+ start' <- require "use in range expression" anySignedType =<< checkExp start+ start_t <- expTypeFully start'+ maybe_step' <- case maybe_step of+ Nothing -> pure Nothing+ Just step -> do+ let warning = warn loc "First and second element of range are identical, this will produce an empty array."+ case (start, step) of+ (Literal x _, Literal y _) -> when (x == y) warning+ (Var x_name _ _, Var y_name _ _) -> when (x_name == y_name) warning+ _ -> pure ()+ Just <$> (unifies "use in range expression" start_t =<< checkExp step)++ let unifyRange e = unifies "use in range expression" start_t =<< checkExp e+ end' <- traverse unifyRange end++ end_t <- case end' of+ DownToExclusive e -> expType e+ ToInclusive e -> expType e+ UpToExclusive e -> expType e++ -- Special case some ranges to give them a known size.+ let warnIfBinding binds size =+ if binds+ then do+ warn (srclocOf size) $+ withIndexLink+ "size-expression-bind"+ "Size expression with binding is replaced by unknown size."+ d <- newRigidDim loc RigidRange "range_dim"+ pure (sizeFromName (qualName d) mempty, Just d)+ else pure (size, Nothing)+ (dim, retext) <-+ case (isInt64 start', isInt64 <$> maybe_step', end') of+ (Just 0, Just (Just 1), UpToExclusive end'')+ | Scalar (Prim (Signed Int64)) <- end_t ->+ warnIfBinding (hasBinding end'') end''+ (Just 0, Nothing, UpToExclusive end'')+ | Scalar (Prim (Signed Int64)) <- end_t ->+ warnIfBinding (hasBinding end'') end''+ (_, Nothing, UpToExclusive end'')+ | Scalar (Prim (Signed Int64)) <- end_t ->+ warnIfBinding (hasBinding end'' || hasBinding start') $ sizeMinus end'' start'+ (_, Nothing, ToInclusive end'')+ -- No stride means we assume a stride of one.+ | Scalar (Prim (Signed Int64)) <- end_t ->+ warnIfBinding (hasBinding end'' || hasBinding start') $ sizeMinusInc end'' start'+ (Just 1, Just (Just 2), ToInclusive end'')+ | Scalar (Prim (Signed Int64)) <- end_t ->+ warnIfBinding (hasBinding end'') end''+ _ -> do+ d <- newRigidDim loc RigidRange "range_dim"+ pure (sizeFromName (qualName d) mempty, Just d)++ t <- arrayOfM loc start_t (Shape [dim])+ let res = AppRes t (maybeToList retext)++ pure $ AppExp (Range start' maybe_step' end' loc) (Info res)+ where+ i64 = Scalar $ Prim $ Signed Int64+ mkBinOp op t x y =+ AppExp+ ( BinOp+ (qualName (intrinsicVar op), mempty)+ sizeBinOpInfo+ (x, Info Nothing)+ (y, Info Nothing)+ mempty+ )+ (Info $ AppRes t [])+ mkSub = mkBinOp "-" i64+ mkAdd = mkBinOp "+" i64+ sizeMinus j i = j `mkSub` i+ sizeMinusInc j i = (j `mkSub` i) `mkAdd` sizeFromInteger 1 mempty+ sizeBinOpInfo = Info $ foldFunType [i64, i64] $ RetType [] i64+checkExp (Ascript e te loc) = do+ (te', e') <- checkAscript loc te e+ pure $ Ascript e' te' loc+checkExp (Coerce e te NoInfo loc) = do+ (te', te_t, e') <- checkCoerce loc te e+ t <- expTypeFully e'+ t' <- matchDims (const . const pure) t te_t+ pure $ Coerce e' te' (Info t') loc+checkExp (AppExp (BinOp (op, oploc) NoInfo (e1, _) (e2, _) loc) NoInfo) = do+ (op', ftype) <- lookupVar oploc op+ e1' <- checkExp e1+ e2' <- checkExp e2++ -- Note that the application to the first operand cannot fix any+ -- existential sizes, because it must by necessity be a function.+ (_, _, rt, p1_ext, _) <- checkApply loc (Just op', 0) ftype e1'+ (_, _, rt', p2_ext, retext) <- checkApply loc (Just op', 1) rt e2'++ pure $+ AppExp+ ( BinOp+ (op', oploc)+ (Info ftype)+ (e1', Info p1_ext)+ (e2', Info p2_ext)+ loc+ )+ (Info (AppRes rt' retext))+checkExp (Project k e NoInfo loc) = do+ e' <- checkExp e+ t <- expType e'+ kt <- mustHaveField (mkUsage loc $ docText $ "projection of field " <> dquotes (pretty k)) k t+ pure $ Project k e' (Info kt) loc+checkExp (AppExp (If e1 e2 e3 loc) _) = do+ e1' <- checkExp e1+ e2' <- checkExp e2+ e3' <- checkExp e3++ let bool = Scalar $ Prim Bool+ e1_t <- expType e1'+ onFailure (CheckingRequired [bool] e1_t) $+ unify (mkUsage e1' "use as 'if' condition") bool e1_t++ (brancht, retext) <- unifyBranches loc e2' e3'++ zeroOrderType+ (mkUsage loc "returning value of this type from 'if' expression")+ "type returned from branch"+ brancht++ pure $ AppExp (If e1' e2' e3' loc) (Info $ AppRes brancht retext)+checkExp (Parens e loc) =+ Parens <$> checkExp e <*> pure loc+checkExp (QualParens (modname, modnameloc) e loc) = do+ (modname', mod) <- lookupMod loc modname+ case mod of+ ModEnv env -> local (`withEnv` qualifyEnv modname' env) $ do+ e' <- checkExp e+ pure $ QualParens (modname', modnameloc) e' loc+ ModFun {} ->+ typeError loc mempty . withIndexLink "module-is-parametric" $+ "Module" <+> pretty modname <+> " is a parametric module."+ where+ qualifyEnv modname' env =+ env {envNameMap = M.map (qualify' modname') $ envNameMap env}+ qualify' modname' (QualName qs name) =+ QualName (qualQuals modname' ++ [qualLeaf modname'] ++ qs) name+-- Handle common case specially for efficiency.+checkExp (Var qn@(QualName [] _) NoInfo loc) = do+ (qn', t) <- lookupVar loc qn+ pure $ Var qn' (Info t) loc+checkExp (Var qn NoInfo loc) = do+ -- The qualifiers of a variable is divided into two parts: first a+ -- possibly-empty sequence of module qualifiers, followed by a+ -- possible-empty sequence of record field accesses. We use scope+ -- information to perform the split, by taking qualifiers off the+ -- end until we find a module.++ (qn', t, fields) <- findRootVar (qualQuals qn) (qualLeaf qn)++ foldM checkField (Var qn' (Info t) loc) fields+ where+ findRootVar qs name =+ (whenFound <$> lookupVar loc (QualName qs name)) `catchError` notFound qs name++ whenFound (qn', t) = (qn', t, [])++ notFound qs name err+ | null qs = throwError err+ | otherwise = do+ (qn', t, fields) <-+ findRootVar (init qs) (last qs)+ `catchError` const (throwError err)+ pure (qn', t, fields ++ [name])++ checkField e k = do+ t <- expType e+ let usage = mkUsage loc $ docText $ "projection of field " <> dquotes (pretty k)+ kt <- mustHaveField usage k t+ pure $ Project k e (Info kt) loc+checkExp (Negate arg loc) = do+ arg' <- require "numeric negation" anyNumberType =<< checkExp arg+ pure $ Negate arg' loc+checkExp (Not arg loc) = do+ arg' <- require "logical negation" (Bool : anyIntType) =<< checkExp arg+ pure $ Not arg' loc+checkExp (AppExp (Apply fe args loc) NoInfo) = do+ fe' <- checkExp fe+ args' <- mapM (checkExp . snd) args+ t <- expType fe'+ let fname =+ case fe' of+ Var v _ _ -> Just v+ _ -> Nothing+ ((_, exts, rt), args'') <- mapAccumLM (onArg fname) (0, [], t) args'++ pure $ AppExp (Apply fe' args'' loc) $ Info $ AppRes rt exts+ where+ onArg fname (i, all_exts, t) arg' = do+ (d1, _, rt, argext, exts) <- checkApply loc (fname, i) t arg'+ pure+ ( (i + 1, all_exts <> exts, rt),+ (Info (d1, argext), arg')+ )+checkExp (AppExp (LetPat sizes pat e body loc) _) = do+ e' <- checkExp e++ -- Not technically an ascription, but we want the pattern to have+ -- exactly the type of 'e'.+ t <- expType e'+ incLevel . bindingSizes sizes $ \sizes' ->+ bindingPat sizes' pat t $ \pat' -> do+ body' <- checkExp body+ body_t <- expTypeFully body'++ -- If the bound expression is of type i64, then we replace the+ -- pattern name with the expression in the type of the body.+ -- Otherwise, we need to come up with unknown sizes for the+ -- sizes going out of scope.+ t' <- normType t -- Might be overloaded integer until now.+ (body_t', retext) <-+ case (t', patNames pat') of+ (Scalar (Prim (Signed Int64)), [v])+ | not $ hasBinding e' -> do+ let f x = if x == v then Just (ExpSubst e') else Nothing+ pure (applySubst f body_t, [])+ _ ->+ unscopeType loc (patNames pat') body_t++ pure $+ AppExp+ (LetPat sizes' (fmap toStruct pat') e' body' loc)+ (Info $ AppRes body_t' retext)+checkExp (AppExp (LetFun name (tparams, params, maybe_retdecl, NoInfo, e) body loc) _) = do+ (tparams', params', maybe_retdecl', rettype, e') <-+ checkBinding (name, maybe_retdecl, tparams, params, e, loc)++ bindSpaced [(Term, name)] $ do+ name' <- checkName Term name loc++ let entry = BoundV tparams' $ funType params' rettype+ bindF scope =+ scope+ { scopeVtable =+ M.insert name' entry $ scopeVtable scope,+ scopeNameMap =+ M.insert (Term, name) (qualName name') $+ scopeNameMap scope+ }+ body' <- localScope bindF $ checkExp body++ (body_t, ext) <- unscopeType loc [name'] =<< expTypeFully body'++ pure $+ AppExp+ ( LetFun+ name'+ (tparams', params', maybe_retdecl', Info rettype, e')+ body'+ loc+ )+ (Info $ AppRes body_t ext)+checkExp (AppExp (LetWith dest src slice ve body loc) _) = do+ src' <- checkIdent src+ slice' <- checkSlice slice+ (t, _) <- newArrayType (mkUsage src "type of source array") "src" $ sliceDims slice'+ unify (mkUsage loc "type of target array") t $ unInfo $ identType src'++ (elemt, _) <- sliceShape (Just (loc, Nonrigid)) slice' =<< normTypeFully t++ ve' <- unifies "type of target array" elemt =<< checkExp ve++ bindingIdent dest (unInfo (identType src')) $ \dest' -> do+ body' <- checkExp body+ (body_t, ext) <- unscopeType loc [identName dest'] =<< expTypeFully body'+ pure $ AppExp (LetWith dest' src' slice' ve' body' loc) (Info $ AppRes body_t ext)+checkExp (Update src slice ve loc) = do+ slice' <- checkSlice slice+ (t, _) <- newArrayType (mkUsage' src) "src" $ sliceDims slice'+ (elemt, _) <- sliceShape (Just (loc, Nonrigid)) slice' =<< normTypeFully t+ ve' <- unifies "type of target array" elemt =<< checkExp ve+ src' <- unifies "type of target array" t =<< checkExp src+ pure $ Update src' slice' ve' loc++-- Record updates are a bit hacky, because we do not have row typing+-- (yet?). For now, we only permit record updates where we know the+-- full type up to the field we are updating.+checkExp (RecordUpdate src fields ve NoInfo loc) = do+ src' <- checkExp src+ ve' <- checkExp ve+ a <- expTypeFully src'+ foldM_ (flip $ mustHaveField usage) a fields+ ve_t <- expType ve'+ updated_t <- updateField fields ve_t =<< expTypeFully src'+ pure $ RecordUpdate src' fields ve' (Info updated_t) loc+ where+ usage = mkUsage loc "record update"+ updateField [] ve_t src_t = do+ (src_t', _) <- allDimsFreshInType usage Nonrigid "any" src_t+ onFailure (CheckingRecordUpdate fields src_t' ve_t) $+ unify usage src_t' ve_t+ pure ve_t+ updateField (f : fs) ve_t (Scalar (Record m))+ | Just f_t <- M.lookup f m = do+ f_t' <- updateField fs ve_t f_t+ pure $ Scalar $ Record $ M.insert f f_t' m+ updateField _ _ _ =+ typeError loc mempty . withIndexLink "record-type-not-known" $+ "Full type of"+ </> indent 2 (pretty src)+ </> textwrap " is not known at this point. Add a type annotation to the original record to disambiguate."++--+checkExp (AppExp (Index e slice loc) _) = do+ slice' <- checkSlice slice+ (t, _) <- newArrayType (mkUsage' loc) "e" $ sliceDims slice'+ e' <- unifies "being indexed at" t =<< checkExp e+ -- XXX, the RigidSlice here will be overridden in sliceShape with a proper value.+ (t', retext) <-+ sliceShape (Just (loc, Rigid (RigidSlice Nothing ""))) slice'+ =<< expTypeFully e'++ pure $ AppExp (Index e' slice' loc) (Info $ AppRes t' retext)+checkExp (Assert e1 e2 NoInfo loc) = do+ e1' <- require "being asserted" [Bool] =<< checkExp e1+ e2' <- checkExp e2+ pure $ Assert e1' e2' (Info (prettyText e1)) loc+checkExp (Lambda params body rettype_te NoInfo loc) = do+ (params', body', rettype', RetType dims ty) <-+ incLevel . bindingParams [] params $ \_ params' -> do+ rettype_checked <- traverse checkTypeExpNonrigid rettype_te+ let declared_rettype =+ case rettype_checked of+ Just (_, st, _) -> Just st+ Nothing -> Nothing+ body' <- checkFunBody params' body declared_rettype loc+ body_t <- expTypeFully body'++ params'' <- mapM updateTypes params'++ (rettype', rettype_st) <-+ case rettype_checked of+ Just (te, st, ext) ->+ pure (Just te, RetType ext st)+ Nothing -> do+ ret <- inferReturnSizes params'' $ toRes Nonunique body_t+ pure (Nothing, ret)++ pure (params'', body', rettype', rettype_st)++ verifyFunctionParams Nothing params'++ (ty', dims') <- unscopeType loc dims ty++ pure $ Lambda params' body' rettype' (Info (RetType dims' ty')) loc+ where+ -- Inferring the sizes of the return type of a lambda is a lot+ -- like let-generalisation. We wish to remove any rigid sizes+ -- that were created when checking the body, except for those that+ -- are visible in types that existed before we entered the body,+ -- are parameters, or are used in parameters.+ inferReturnSizes params' ret = do+ cur_lvl <- curLevel+ let named (Named x, _, _) = Just x+ named (Unnamed, _, _) = Nothing+ param_names = mapMaybe (named . patternParam) params'+ pos_sizes =+ sizeNamesPos $ funType params' $ RetType [] ret+ hide k (lvl, _) =+ lvl >= cur_lvl && k `notElem` param_names && k `S.notMember` pos_sizes++ hidden_sizes <-+ S.fromList . M.keys . M.filterWithKey hide <$> getConstraints++ let onDim name+ | name `S.member` hidden_sizes = S.singleton name+ onDim _ = mempty++ pure $ RetType (S.toList $ foldMap onDim $ fvVars $ freeInType ret) ret+checkExp (OpSection op _ loc) = do+ (op', ftype) <- lookupVar loc op+ pure $ OpSection op' (Info ftype) loc+checkExp (OpSectionLeft op _ e _ _ loc) = do+ (op', ftype) <- lookupVar loc op+ e' <- checkExp e+ (_, t1, rt, argext, retext) <- checkApply loc (Just op', 0) ftype e'+ case (ftype, rt) of+ (Scalar (Arrow _ m1 d1 _ _), Scalar (Arrow _ m2 d2 t2 rettype)) ->+ pure $+ OpSectionLeft+ op'+ (Info ftype)+ e'+ (Info (m1, toParam d1 t1, argext), Info (m2, toParam d2 t2))+ (Info rettype, Info retext)+ loc+ _ ->+ typeError loc mempty $+ "Operator section with invalid operator of type" <+> pretty ftype+checkExp (OpSectionRight op _ e _ NoInfo loc) = do+ (op', ftype) <- lookupVar loc op+ e' <- checkExp e+ case ftype of+ Scalar (Arrow _ m1 d1 t1 (RetType [] (Scalar (Arrow _ m2 d2 t2 (RetType dims2 ret))))) -> do+ (_, t2', arrow', argext, _) <-+ checkApply+ loc+ (Just op', 1)+ (Scalar $ Arrow mempty m2 d2 t2 $ RetType [] $ Scalar $ Arrow Nonunique m1 d1 t1 $ RetType dims2 ret)+ e'+ case arrow' of+ Scalar (Arrow _ _ _ t1' (RetType dims2' ret')) ->+ pure $+ OpSectionRight+ op'+ (Info ftype)+ e'+ (Info (m1, toParam d1 t1'), Info (m2, toParam d2 t2', argext))+ (Info $ RetType dims2' ret')+ loc+ _ -> error $ "OpSectionRight: impossible type\n" <> prettyString arrow'+ _ ->+ typeError loc mempty $+ "Operator section with invalid operator of type" <+> pretty ftype+checkExp (ProjectSection fields NoInfo loc) = do+ a <- newTypeVar loc "a"+ let usage = mkUsage loc "projection at"+ b <- foldM (flip $ mustHaveField usage) a fields+ let ft = Scalar $ Arrow mempty Unnamed Observe a $ RetType [] $ toRes Nonunique b+ pure $ ProjectSection fields (Info ft) loc+checkExp (IndexSection slice NoInfo loc) = do+ slice' <- checkSlice slice+ (t, _) <- newArrayType (mkUsage' loc) "e" $ sliceDims slice'+ (t', retext) <- sliceShape Nothing slice' t+ let ft = Scalar $ Arrow mempty Unnamed Observe t $ RetType retext $ toRes Nonunique t'+ pure $ IndexSection slice' (Info ft) loc+checkExp (AppExp (DoLoop _ mergepat mergeexp form loopbody loc) _) = do+ ((sparams, mergepat', mergeexp', form', loopbody'), appres) <-+ checkDoLoop checkExp (mergepat, mergeexp, form, loopbody) loc+ pure $+ AppExp+ (DoLoop sparams mergepat' mergeexp' form' loopbody' loc)+ (Info appres)+checkExp (Constr name es NoInfo loc) = do+ t <- newTypeVar loc "t"+ es' <- mapM checkExp es+ ets <- mapM expTypeFully es'+ mustHaveConstr (mkUsage loc "use of constructor") name t ets+ pure $ Constr name es' (Info t) loc+checkExp (AppExp (Match e cs loc) _) = do+ e' <- checkExp e+ mt <- expTypeFully e'+ (cs', t, retext) <- checkCases mt cs+ zeroOrderType+ (mkUsage loc "being returned 'match'")+ "type returned from pattern match"+ t+ pure $ AppExp (Match e' cs' loc) (Info $ AppRes t retext)+checkExp (Attr info e loc) =+ Attr <$> checkAttr info <*> checkExp e <*> pure loc++checkCases ::+ StructType ->+ NE.NonEmpty (CaseBase NoInfo Name) ->+ TermTypeM (NE.NonEmpty (CaseBase Info VName), StructType, [VName])+checkCases mt rest_cs =+ case NE.uncons rest_cs of+ (c, Nothing) -> do+ (c', t, retext) <- checkCase mt c+ pure (NE.singleton c', t, retext)+ (c, Just cs) -> do+ ((c', c_t, _), (cs', cs_t, _)) <-+ (,) <$> checkCase mt c <*> checkCases mt cs+ (brancht, retext) <- unifyBranchTypes (srclocOf c) c_t cs_t+ pure (NE.cons c' cs', brancht, retext)++checkCase ::+ StructType ->+ CaseBase NoInfo Name ->+ TermTypeM (CaseBase Info VName, StructType, [VName])+checkCase mt (CasePat p e loc) =+ bindingPat [] p mt $ \p' -> do+ e' <- checkExp e+ e_t <- expTypeFully e'+ (e_t', retext) <- unscopeType loc (patNames p') e_t+ pure (CasePat (fmap toStruct p') e' loc, e_t', retext)++-- | An unmatched pattern. Used in in the generation of+-- unmatched pattern warnings by the type checker.+data Unmatched p+ = UnmatchedNum p [PatLit]+ | UnmatchedBool p+ | UnmatchedConstr p+ | Unmatched p+ deriving (Functor, Show)++instance Pretty (Unmatched (Pat StructType)) where+ pretty um = case um of+ (UnmatchedNum p nums) -> pretty' p <+> "where p is not one of" <+> pretty nums+ (UnmatchedBool p) -> pretty' p+ (UnmatchedConstr p) -> pretty' p+ (Unmatched p) -> pretty' p+ where+ pretty' (PatAscription p t _) = pretty p <> ":" <+> pretty t+ pretty' (PatParens p _) = parens $ pretty' p+ pretty' (PatAttr _ p _) = parens $ pretty' p+ pretty' (Id v _ _) = prettyName v+ pretty' (TuplePat pats _) = parens $ commasep $ map pretty' pats+ pretty' (RecordPat fs _) = braces $ commasep $ map ppField fs+ where+ ppField (name, t) = pretty (nameToString name) <> equals <> pretty' t+ pretty' Wildcard {} = "_"+ pretty' (PatLit e _ _) = pretty e+ pretty' (PatConstr n _ ps _) = "#" <> pretty n <+> sep (map pretty' ps)++checkIdent :: IdentBase NoInfo Name StructType -> TermTypeM (Ident StructType)+checkIdent (Ident name _ loc) = do+ (QualName _ name', vt) <- lookupVar loc (qualName name)+ pure $ Ident name' (Info vt) loc++checkSlice :: UncheckedSlice -> TermTypeM [DimIndex]+checkSlice = mapM checkDimIndex+ where+ checkDimIndex (DimFix i) = do+ DimFix <$> (require "use as index" anySignedType =<< checkExp i)+ checkDimIndex (DimSlice i j s) =+ DimSlice <$> check i <*> check j <*> check s++ check =+ maybe (pure Nothing) $+ fmap Just . unifies "use as index" (Scalar $ Prim $ Signed Int64) <=< checkExp++-- The number of dimensions affected by this slice (so the minimum+-- rank of the array we are slicing).+sliceDims :: [DimIndex] -> Int+sliceDims = length++instantiateDimsInReturnType ::+ SrcLoc ->+ Maybe (QualName VName) ->+ ResRetType ->+ TermTypeM (ResType, [VName])+instantiateDimsInReturnType loc fname (RetType dims t)+ | null dims =+ pure (t, mempty)+ | otherwise = do+ dims' <- mapM new dims+ pure (first (onDim $ zip dims $ map (ExpSubst . (`sizeFromName` loc) . qualName) dims') t, dims')+ where+ new =+ newRigidDim loc (RigidRet fname)+ . nameFromString+ . takeWhile isAscii+ . baseString+ onDim dims' = applySubst (`lookup` dims')++-- Some information about the function/operator we are trying to+-- apply, and how many arguments it has previously accepted. Used for+-- generating nicer type errors.+type ApplyOp = (Maybe (QualName VName), Int)++-- | Extract all those names that are bound inside the type.+boundInsideType :: TypeBase Size as -> S.Set VName+boundInsideType (Array _ _ t) = boundInsideType (Scalar t)+boundInsideType (Scalar Prim {}) = mempty+boundInsideType (Scalar (TypeVar _ _ targs)) = foldMap f targs+ where+ f (TypeArgType t) = boundInsideType t+ f TypeArgDim {} = mempty+boundInsideType (Scalar (Record fs)) = foldMap boundInsideType fs+boundInsideType (Scalar (Sum cs)) = foldMap (foldMap boundInsideType) cs+boundInsideType (Scalar (Arrow _ pn _ t1 (RetType dims t2))) =+ pn' <> boundInsideType t1 <> S.fromList dims <> boundInsideType t2+ where+ pn' = case pn of+ Unnamed -> mempty+ Named v -> S.singleton v++-- Returns the sizes of the immediate type produced,+-- the sizes of parameter types, and the sizes of return types.+dimUses :: TypeBase Size u -> (Names, Names)+dimUses = flip execState mempty . traverseDims f+ where+ f bound _ (Var v _ _) | qualLeaf v `S.member` bound = pure ()+ f _ PosImmediate (Var v _ _) = modify ((S.singleton (qualLeaf v), mempty) <>)+ f _ PosParam (Var v _ _) = modify ((mempty, S.singleton (qualLeaf v)) <>)+ f _ _ _ = pure ()++checkApply ::+ SrcLoc ->+ ApplyOp ->+ StructType ->+ Exp ->+ TermTypeM (Diet, StructType, StructType, Maybe VName, [VName])+checkApply loc (fname, _) (Scalar (Arrow _ pname d1 tp1 tp2)) argexp = do+ let argtype = typeOf argexp+ onFailure (CheckingApply fname argexp tp1 argtype) $ do+ unify (mkUsage argexp "use as function argument") tp1 argtype++ -- Perform substitutions of instantiated variables in the types.+ (tp2', ext) <- instantiateDimsInReturnType loc fname =<< normTypeFully tp2+ argtype' <- normTypeFully argtype++ -- Check whether this would produce an impossible return type.+ let (tp2_produced_dims, tp2_paramdims) = dimUses $ toStruct tp2'+ problematic = S.fromList ext <> boundInsideType argtype'+ problem = any (`S.member` problematic) (tp2_paramdims `S.difference` tp2_produced_dims)+ when (not (S.null problematic) && problem) $ do+ typeError loc mempty . withIndexLink "existential-param-ret" $+ "Existential size would appear in function parameter of return type:"+ </> indent 2 (pretty (RetType ext tp2'))+ </> textwrap "This is usually because a higher-order function is used with functional arguments that return existential sizes or locally named sizes, which are then used as parameters of other function arguments."++ (argext, tp2'') <-+ case pname of+ Named pname'+ | S.member pname' (fvVars $ freeInType tp2') ->+ if hasBinding argexp+ then do+ warn (srclocOf argexp) $+ withIndexLink+ "size-expression-bind"+ "Size expression with binding is replaced by unknown size."+ d <- newRigidDim argexp (RigidArg fname $ prettyTextOneLine $ bareExp argexp) "n"+ let parsubst v =+ if v == pname'+ then Just $ ExpSubst $ sizeFromName (qualName d) $ srclocOf argexp+ else Nothing+ pure (Just d, applySubst parsubst $ toStruct tp2')+ else+ let parsubst v =+ if v == pname'+ then Just $ ExpSubst $ fromMaybe argexp $ stripExp argexp+ else Nothing+ in pure (Nothing, applySubst parsubst $ toStruct tp2')+ _ -> pure (Nothing, toStruct tp2')++ pure (d1, tp1, tp2'', argext, ext)+checkApply loc fname tfun@(Scalar TypeVar {}) arg = do+ tv <- newTypeVar loc "b"+ unify (mkUsage loc "use as function") tfun $+ Scalar (Arrow mempty Unnamed Observe (typeOf arg) $ RetType [] $ paramToRes tv)+ tfun' <- normType tfun+ checkApply loc fname tfun' arg+checkApply loc (fname, prev_applied) ftype argexp = do+ let fname' = maybe "expression" (dquotes . pretty) fname++ typeError loc mempty $+ if prev_applied == 0+ then+ "Cannot apply"+ <+> fname'+ <+> "as function, as it has type:"+ </> indent 2 (pretty ftype)+ else+ "Cannot apply"+ <+> fname'+ <+> "to argument #" <> pretty (prev_applied + 1)+ <+> dquotes (shorten $ group $ pretty argexp) <> ","+ </> "as"+ <+> fname'+ <+> "only takes"+ <+> pretty prev_applied+ <+> arguments <> "."+ where+ arguments+ | prev_applied == 1 = "argument"+ | otherwise = "arguments"++-- | Type-check a single expression in isolation. This expression may+-- turn out to be polymorphic, in which case the list of type+-- parameters will be non-empty.+checkOneExp :: UncheckedExp -> TypeM ([TypeParam], Exp)+checkOneExp e = runTermTypeM checkExp $ do+ e' <- checkExp e+ let t = typeOf e'+ (tparams, _, _) <-+ letGeneralise (nameFromString "<exp>") (srclocOf e) [] [] $ toRes Nonunique t+ fixOverloadedTypes $ typeVars t+ e'' <- updateTypes e'+ localChecks e''+ causalityCheck e''+ pure (tparams, e'')++-- | Type-check a single size expression in isolation. This expression may+-- turn out to be polymorphic, in which case it is unified with i64.+checkSizeExp :: UncheckedExp -> TypeM Exp+checkSizeExp e = runTermTypeM checkExp $ do+ e' <- checkExp e+ let t = typeOf e'+ when (hasBinding e') $+ typeError (srclocOf e') mempty . withIndexLink "size-expression-bind" $+ "Size expression with binding is forbidden."+ unify (mkUsage e' "Size expression") t (Scalar (Prim (Signed Int64)))+ updateTypes e'++-- Verify that all sum type constructors and empty array literals have+-- a size that is known (rigid or a type parameter). This is to+-- ensure that we can actually determine their shape at run-time.+causalityCheck :: Exp -> TermTypeM ()+causalityCheck binding_body = do+ constraints <- getConstraints++ let checkCausality what known t loc+ | (d, dloc) : _ <-+ mapMaybe (unknown constraints known) $+ S.toList (fvVars $ freeInType t) =+ Just $ lift $ causality what (locOf loc) d dloc t+ | otherwise = Nothing++ checkParamCausality known p =+ checkCausality (pretty p) known (patternType p) (locOf p)++ collectingNewKnown = lift . flip execStateT mempty++ onExp ::+ S.Set VName ->+ Exp ->+ StateT (S.Set VName) (Either TypeError) Exp++ onExp known (Var v (Info t) loc)+ | Just bad <- checkCausality (dquotes (pretty v)) known t loc =+ bad+ onExp known (ProjectSection _ (Info t) loc)+ | Just bad <- checkCausality "projection section" known t loc =+ bad+ onExp known (IndexSection _ (Info t) loc)+ | Just bad <- checkCausality "projection section" known t loc =+ bad+ onExp known (OpSectionRight _ (Info t) _ _ _ loc)+ | Just bad <- checkCausality "operator section" known t loc =+ bad+ onExp known (OpSectionLeft _ (Info t) _ _ _ loc)+ | Just bad <- checkCausality "operator section" known t loc =+ bad+ onExp known (ArrayLit [] (Info t) loc)+ | Just bad <- checkCausality "empty array" known t loc =+ bad+ onExp known (Hole (Info t) loc)+ | Just bad <- checkCausality "hole" known t loc =+ bad+ onExp known e@(Lambda params body _ _ _)+ | bad : _ <- mapMaybe (checkParamCausality known) params =+ bad+ | otherwise = do+ -- Existentials coming into existence in the lambda body+ -- are not known outside of it.+ void $ collectingNewKnown $ onExp known body+ pure e+ onExp known e@(AppExp (LetPat _ _ bindee_e body_e _) (Info res)) = do+ sequencePoint known bindee_e body_e $ appResExt res+ pure e+ onExp known e@(AppExp (Match scrutinee cs _) (Info res)) = do+ new_known <- collectingNewKnown $ onExp known scrutinee+ void $ recurse (new_known <> known) cs+ modify ((new_known <> S.fromList (appResExt res)) <>)+ pure e+ onExp known e@(AppExp (Apply f args _) (Info res)) = do+ seqArgs known $ reverse $ NE.toList args+ pure e+ where+ seqArgs known' [] = do+ void $ onExp known' f+ modify (S.fromList (appResExt res) <>)+ seqArgs known' ((Info (_, p), x) : xs) = do+ new_known <- collectingNewKnown $ onExp known' x+ void $ seqArgs (new_known <> known') xs+ modify ((new_known <> S.fromList (maybeToList p)) <>)+ onExp known e@(Constr v args (Info t) loc) = do+ seqArgs known args+ pure e+ where+ seqArgs known' []+ | Just bad <- checkCausality (dquotes ("#" <> pretty v)) known' t loc =+ bad+ | otherwise =+ pure ()+ seqArgs known' (x : xs) = do+ new_known <- collectingNewKnown $ onExp known' x+ void $ seqArgs (new_known <> known') xs+ modify (new_known <>)+ onExp+ known+ e@(AppExp (BinOp (f, floc) ft (x, Info xp) (y, Info yp) _) (Info res)) = do+ args_known <-+ collectingNewKnown $ sequencePoint known x y $ catMaybes [xp, yp]+ void $ onExp (args_known <> known) (Var f ft floc)+ modify ((args_known <> S.fromList (appResExt res)) <>)+ pure e+ onExp known e@(AppExp e' (Info res)) = do+ recurse known e'+ modify (<> S.fromList (appResExt res))+ pure e+ onExp known e = do+ recurse known e+ pure e++ recurse known = void . astMap mapper+ where+ mapper = identityMapper {mapOnExp = onExp known}++ sequencePoint known x y ext = do+ new_known <- collectingNewKnown $ onExp known x+ void $ onExp (new_known <> known) y+ modify ((new_known <> S.fromList ext) <>)++ either throwError (const $ pure ()) $+ evalStateT (onExp mempty binding_body) mempty+ where+ unknown constraints known v = do+ guard $ v `S.notMember` known+ loc <- case snd <$> M.lookup v constraints of+ Just (UnknownSize loc _) -> Just loc+ _ -> Nothing+ pure (v, loc)++ causality what loc d dloc t =+ Left . TypeError loc mempty . withIndexLink "causality-check" $+ "Causality check: size"+ <+> dquotes (prettyName d)+ <+> "needed for type of"+ <+> what <> colon+ </> indent 2 (pretty t)+ </> "But"+ <+> dquotes (prettyName d)+ <+> "is computed at"+ <+> pretty (locStrRel loc dloc) <> "."+ </> ""+ </> "Hint:"+ <+> align+ ( textwrap "Bind the expression producing"+ <+> dquotes (prettyName d)+ <+> "with 'let' beforehand."+ )++-- | Traverse the expression, emitting warnings and errors for various+-- problems:+--+-- * Unmatched cases.+--+-- * If any of the literals overflow their inferred types. Note:+-- currently unable to detect float underflow (such as 1e-400 -> 0)+localChecks :: Exp -> TermTypeM ()+localChecks = void . check+ where+ check e@(AppExp (Match _ cs loc) _) = do+ let ps = fmap (\(CasePat p _ _) -> p) cs+ case unmatched $ NE.toList ps of+ [] -> recurse e+ ps' ->+ typeError loc mempty . withIndexLink "unmatched-cases" $+ "Unmatched cases in match expression:"+ </> indent 2 (stack (map pretty ps'))+ check e@(IntLit x ty loc) =+ e <$ case ty of+ Info (Scalar (Prim t)) -> errorBounds (inBoundsI x t) x t loc+ _ -> error "Inferred type of int literal is not a number"+ check e@(FloatLit x ty loc) =+ e <$ case ty of+ Info (Scalar (Prim (FloatType t))) -> errorBounds (inBoundsF x t) x t loc+ _ -> error "Inferred type of float literal is not a float"+ check e@(Negate (IntLit x ty loc1) loc2) =+ e <$ case ty of+ Info (Scalar (Prim t)) -> errorBounds (inBoundsI (-x) t) (-x) t (loc1 <> loc2)+ _ -> error "Inferred type of int literal is not a number"+ check e@(AppExp (BinOp (QualName [] v, _) _ (x, _) _ loc) _)+ | baseName v == "==",+ Array {} <- typeOf x,+ baseTag v <= maxIntrinsicTag = do+ warn loc $+ textwrap+ "Comparing arrays with \"==\" is deprecated and will stop working in a future revision of the language."+ recurse e+ check e = recurse e+ recurse = astMap identityMapper {mapOnExp = check}++ bitWidth ty = 8 * intByteSize ty :: Int++ inBoundsI x (Signed t) = x >= -2 ^ (bitWidth t - 1) && x < 2 ^ (bitWidth t - 1)+ inBoundsI x (Unsigned t) = x >= 0 && x < 2 ^ bitWidth t+ inBoundsI x (FloatType Float16) = not $ isInfinite (fromIntegral x :: Half)+ inBoundsI x (FloatType Float32) = not $ isInfinite (fromIntegral x :: Float)+ inBoundsI x (FloatType Float64) = not $ isInfinite (fromIntegral x :: Double)+ inBoundsI _ Bool = error "Inferred type of int literal is not a number"+ inBoundsF x Float16 = not $ isInfinite (realToFrac x :: Float)+ inBoundsF x Float32 = not $ isInfinite (realToFrac x :: Float)+ inBoundsF x Float64 = not $ isInfinite x++ errorBounds inBounds x ty loc =+ unless inBounds $+ typeError loc mempty . withIndexLink "literal-out-of-bounds" $+ "Literal "+ <> pretty x+ <> " out of bounds for inferred type "+ <> pretty ty+ <> "."++-- | Type-check a top-level (or module-level) function definition.+-- Despite the name, this is also used for checking constant+-- definitions, by treating them as 0-ary functions.+checkFunDef ::+ ( Name,+ Maybe UncheckedTypeExp,+ [UncheckedTypeParam],+ [UncheckedPat ParamType],+ UncheckedExp,+ SrcLoc+ ) ->+ TypeM+ ( VName,+ [TypeParam],+ [Pat ParamType],+ Maybe (TypeExp Info VName),+ ResRetType,+ Exp+ )+checkFunDef (fname, maybe_retdecl, tparams, params, body, loc) =+ runTermTypeM checkExp $ do+ (tparams', params', maybe_retdecl', RetType dims rettype', body') <-+ checkBinding (fname, maybe_retdecl, tparams, params, body, loc)++ -- Since this is a top-level function, we also resolve overloaded+ -- types, using either defaults or complaining about ambiguities.+ fixOverloadedTypes $+ typeVars rettype' <> foldMap (typeVars . patternType) params'++ -- Then replace all inferred types in the body and parameters.+ body'' <- updateTypes body'+ params'' <- updateTypes params'+ maybe_retdecl'' <- traverse updateTypes maybe_retdecl'+ rettype'' <- normTypeFully rettype'++ -- Check if the function body can actually be evaluated.+ causalityCheck body''++ -- Check for various problems.+ localChecks body''++ bindSpaced [(Term, fname)] $ do+ fname' <- checkName Term fname loc+ when (fname `elem` doNotShadow) $+ typeError loc mempty . withIndexLink "may-not-be-redefined" $+ "The" <+> prettyName fname <+> "operator may not be redefined."++ let ((body''', updated_ret), errors) =+ Consumption.checkValDef+ ( fname',+ params'',+ body'',+ RetType dims rettype'',+ maybe_retdecl'',+ loc+ )++ mapM_ throwError errors++ pure (fname', tparams', params'', maybe_retdecl'', updated_ret, body''')++-- | This is "fixing" as in "setting them", not "correcting them". We+-- only make very conservative fixing.+fixOverloadedTypes :: Names -> TermTypeM ()+fixOverloadedTypes tyvars_at_toplevel =+ getConstraints >>= mapM_ fixOverloaded . M.toList . M.map snd+ where+ fixOverloaded (v, Overloaded ots usage)+ | Signed Int32 `elem` ots = do+ unify usage (Scalar (TypeVar mempty (qualName v) [])) $+ Scalar (Prim $ Signed Int32)+ when (v `S.member` tyvars_at_toplevel) $+ warn usage "Defaulting ambiguous type to i32."+ | FloatType Float64 `elem` ots = do+ unify usage (Scalar (TypeVar mempty (qualName v) [])) $+ Scalar (Prim $ FloatType Float64)+ when (v `S.member` tyvars_at_toplevel) $+ warn usage "Defaulting ambiguous type to f64."+ | otherwise =+ typeError usage mempty . withIndexLink "ambiguous-type" $+ "Type is ambiguous (could be one of"+ <+> commasep (map pretty ots) <> ")."+ </> "Add a type annotation to disambiguate the type."+ fixOverloaded (v, NoConstraint _ usage) = do+ -- See #1552.+ unify usage (Scalar (TypeVar mempty (qualName v) [])) $+ Scalar (tupleRecord [])+ when (v `S.member` tyvars_at_toplevel) $+ warn usage "Defaulting ambiguous type to ()."+ fixOverloaded (_, Equality usage) =+ typeError usage mempty . withIndexLink "ambiguous-type" $+ "Type is ambiguous (must be equality type)."+ </> "Add a type annotation to disambiguate the type."+ fixOverloaded (_, HasFields _ fs usage) =+ typeError usage mempty . withIndexLink "ambiguous-type" $+ "Type is ambiguous. Must be record with fields:"+ </> indent 2 (stack $ map field $ M.toList fs)+ </> "Add a type annotation to disambiguate the type."+ where+ field (l, t) = pretty l <> colon <+> align (pretty t)+ fixOverloaded (_, HasConstrs _ cs usage) =+ typeError usage mempty . withIndexLink "ambiguous-type" $+ "Type is ambiguous (must be a sum type with constructors:"+ <+> pretty (Sum cs) <> ")."+ </> "Add a type annotation to disambiguate the type."+ fixOverloaded (v, Size Nothing (Usage Nothing loc)) =+ typeError loc mempty . withIndexLink "ambiguous-size" $+ "Ambiguous size" <+> dquotes (prettyName v) <> "."+ fixOverloaded (v, Size Nothing (Usage (Just u) loc)) =+ typeError loc mempty . withIndexLink "ambiguous-size" $+ "Ambiguous size" <+> dquotes (prettyName v) <+> "arising from" <+> pretty u <> "."+ fixOverloaded _ = pure ()++hiddenParamNames :: [Pat ParamType] -> [VName]+hiddenParamNames params = hidden+ where+ param_all_names = mconcat $ map patNames params+ named (Named x, _, _) = Just x+ named (Unnamed, _, _) = Nothing+ param_names =+ S.fromList $ mapMaybe (named . patternParam) params+ hidden = filter (`notElem` param_names) param_all_names++inferredReturnType :: SrcLoc -> [Pat ParamType] -> StructType -> TermTypeM StructType+inferredReturnType loc params t = do+ -- The inferred type may refer to names that are bound by the+ -- parameter patterns, but which will not be visible in the type.+ -- These we must turn into fresh type variables, which will be+ -- existential in the return type.+ fst <$> unscopeType loc hidden_params t+ where+ hidden_params = filter (`elem` hidden) $ foldMap patNames params+ hidden = hiddenParamNames params++checkBinding ::+ ( Name,+ Maybe UncheckedTypeExp,+ [UncheckedTypeParam],+ [UncheckedPat ParamType],+ UncheckedExp,+ SrcLoc+ ) ->+ TermTypeM+ ( [TypeParam],+ [Pat ParamType],+ Maybe (TypeExp Info VName),+ ResRetType,+ Exp+ )+checkBinding (fname, maybe_retdecl, tparams, params, body, loc) =+ incLevel . bindingParams tparams params $ \tparams' params' -> do+ maybe_retdecl' <- traverse checkTypeExpNonrigid maybe_retdecl++ body' <-+ checkFunBody+ params'+ body+ ((\(_, x, _) -> x) <$> maybe_retdecl')+ (maybe loc srclocOf maybe_retdecl)++ params'' <- mapM updateTypes params'+ body_t <- expTypeFully body'++ (maybe_retdecl'', rettype) <- case maybe_retdecl' of+ Just (retdecl', ret, _) -> do+ ret' <- normTypeFully ret+ pure (Just retdecl', ret')+ Nothing+ | null params ->+ pure (Nothing, toRes Nonunique body_t)+ | otherwise -> do+ body_t' <- inferredReturnType loc params'' body_t+ pure (Nothing, toRes Nonunique body_t')++ verifyFunctionParams (Just fname) params''++ (tparams'', params''', rettype') <-+ letGeneralise fname loc tparams' params''+ =<< unscopeUnknown rettype++ pure (tparams'', params''', maybe_retdecl'', rettype', body')++-- | Extract all the shape names that occur in positive position+-- (roughly, left side of an arrow) in a given type.+sizeNamesPos :: TypeBase Size als -> S.Set VName+sizeNamesPos (Scalar (Arrow _ _ _ t1 (RetType _ t2))) = onParam t1 <> sizeNamesPos t2+ where+ onParam :: TypeBase Size als -> S.Set VName+ onParam (Scalar Arrow {}) = mempty+ onParam (Scalar (Record fs)) = mconcat $ map onParam $ M.elems fs+ onParam (Scalar (TypeVar _ _ targs)) = mconcat $ map onTypeArg targs+ onParam t = fvVars $ freeInType t+ onTypeArg (TypeArgDim (Var d _ _)) = S.singleton $ qualLeaf d+ onTypeArg (TypeArgDim _) = mempty+ onTypeArg (TypeArgType t) = onParam t+sizeNamesPos _ = mempty++-- | Verify certain restrictions on function parameters, and bail out+-- on dubious constructions.+--+-- These restrictions apply to all functions (anonymous or otherwise).+-- Top-level functions have further restrictions that are checked+-- during let-generalisation.+verifyFunctionParams :: Maybe Name -> [Pat ParamType] -> TermTypeM ()+verifyFunctionParams fname params =+ onFailure (CheckingParams fname) $+ verifyParams (foldMap patNames params) =<< mapM updateTypes params+ where+ verifyParams forbidden (p : ps)+ | d : _ <- filter (`elem` forbidden) $ S.toList $ fvVars $ freeInPat p =+ typeError p mempty . withIndexLink "inaccessible-size" $+ "Parameter"+ <+> dquotes (pretty p)+ </> "refers to size"+ <+> dquotes (prettyName d)+ <> comma+ </> textwrap "which will not be accessible to the caller"+ <> comma+ </> textwrap "possibly because it is nested in a tuple or record."+ </> textwrap "Consider ascribing an explicit type that does not reference "+ <> dquotes (prettyName d)+ <> "."+ | otherwise = verifyParams forbidden' ps+ where+ forbidden' =+ case patternParam p of+ (Named v, _, _) -> delete v forbidden+ _ -> forbidden+ verifyParams _ [] = pure ()++-- | Move existentials down to the level where they are actually used+-- (i.e. have their "witnesses"). E.g. changes+--+-- @+-- ?[n].bool -> [n]bool+-- @+--+-- to+--+-- @+-- bool -> ?[n].[n]bool+-- @+injectExt :: [VName] -> TypeBase Size u -> RetTypeBase Size u+injectExt [] ret = RetType [] ret+injectExt ext ret = RetType ext_here $ deeper ret+ where+ (immediate, _) = dimUses ret+ (ext_here, ext_there) = partition (`S.member` immediate) ext+ deeper :: TypeBase Size u -> TypeBase Size u+ deeper (Scalar (Prim t)) = Scalar $ Prim t+ deeper (Scalar (Record fs)) = Scalar $ Record $ M.map deeper fs+ deeper (Scalar (Sum cs)) = Scalar $ Sum $ M.map (map deeper) cs+ deeper (Scalar (Arrow als p d1 t1 (RetType t2_ext t2))) =+ Scalar $ Arrow als p d1 t1 $ injectExt (ext_there <> t2_ext) t2+ deeper (Scalar (TypeVar u tn targs)) =+ Scalar $ TypeVar u tn $ map deeperArg targs+ deeper t@Array {} = t++ deeperArg (TypeArgType t) = TypeArgType $ deeper t+ deeperArg (TypeArgDim d) = TypeArgDim d++-- | Find all type variables in the given type that are covered by the+-- constraints, and produce type parameters that close over them.+--+-- The passed-in list of type parameters is always prepended to the+-- produced list of type parameters.+closeOverTypes ::+ Name ->+ SrcLoc ->+ [TypeParam] ->+ [StructType] ->+ ResType ->+ Constraints ->+ TermTypeM ([TypeParam], ResRetType)+closeOverTypes defname defloc tparams paramts ret substs = do+ (more_tparams, retext) <-+ partitionEithers . catMaybes+ <$> mapM closeOver (M.toList $ M.map snd to_close_over)+ let mkExt v =+ case M.lookup v substs of+ Just (_, UnknownSize {}) -> Just v+ _ -> Nothing+ pure+ ( tparams ++ more_tparams,+ injectExt (retext ++ mapMaybe mkExt (S.toList $ fvVars $ freeInType ret)) ret+ )+ where+ -- Diet does not matter here.+ t = foldFunType (map (toParam Observe) paramts) $ RetType [] ret+ to_close_over = M.filterWithKey (\k _ -> k `S.member` visible) substs+ visible = typeVars t <> fvVars (freeInType t)++ (produced_sizes, param_sizes) = dimUses t++ -- Avoid duplicate type parameters.+ closeOver (k, _)+ | k `elem` map typeParamName tparams =+ pure Nothing+ closeOver (k, NoConstraint l usage) =+ pure $ Just $ Left $ TypeParamType l k $ srclocOf usage+ closeOver (k, ParamType l loc) =+ pure $ Just $ Left $ TypeParamType l k loc+ closeOver (k, Size Nothing usage) =+ pure $ Just $ Left $ TypeParamDim k $ srclocOf usage+ closeOver (k, UnknownSize _ _)+ | k `S.member` param_sizes,+ k `S.notMember` produced_sizes = do+ notes <- dimNotes defloc $ sizeFromName (qualName k) mempty+ typeError defloc notes . withIndexLink "unknown-param-def" $+ "Unknown size"+ <+> dquotes (prettyName k)+ <+> "in parameter of"+ <+> dquotes (prettyName defname)+ <> ", which is inferred as:"+ </> indent 2 (pretty t)+ | k `S.member` produced_sizes =+ pure $ Just $ Right k+ closeOver (_, _) =+ pure Nothing++letGeneralise ::+ Name ->+ SrcLoc ->+ [TypeParam] ->+ [Pat ParamType] ->+ ResType ->+ TermTypeM ([TypeParam], [Pat ParamType], ResRetType)+letGeneralise defname defloc tparams params rettype =+ onFailure (CheckingLetGeneralise defname) $ do+ now_substs <- getConstraints++ -- Candidates for let-generalisation are those type variables that+ --+ -- (1) were not known before we checked this function, and+ --+ -- (2) are not used in the (new) definition of any type variables+ -- known before we checked this function.+ --+ -- (3) are not referenced from an overloaded type (for example,+ -- are the element types of an incompletely resolved record type).+ -- This is a bit more restrictive than I'd like, and SML for+ -- example does not have this restriction.+ --+ -- Criteria (1) and (2) is implemented by looking at the binding+ -- level of the type variables.+ let keep_type_vars = overloadedTypeVars now_substs++ cur_lvl <- curLevel+ let candidate k (lvl, _) = (k `S.notMember` keep_type_vars) && lvl >= cur_lvl+ new_substs = M.filterWithKey candidate now_substs++ (tparams', RetType ret_dims rettype') <-+ closeOverTypes+ defname+ defloc+ tparams+ (map patternStructType params)+ rettype+ new_substs++ rettype'' <- updateTypes rettype'++ let used_sizes =+ freeInType rettype'' <> foldMap (freeInType . patternType) params+ case filter ((`S.notMember` fvVars used_sizes) . typeParamName) $+ filter isSizeParam tparams' of+ [] -> pure ()+ tp : _ -> unusedSize $ SizeBinder (typeParamName tp) (srclocOf tp)++ -- We keep those type variables that were not closed over by+ -- let-generalisation.+ modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` map typeParamName tparams'++ pure (tparams', params, RetType ret_dims rettype'')++checkFunBody ::+ [Pat ParamType] ->+ UncheckedExp ->+ Maybe ResType ->+ SrcLoc ->+ TermTypeM Exp+checkFunBody params body maybe_rettype loc = do+ body' <- checkExp body++ -- Unify body return type with return annotation, if one exists.+ case maybe_rettype of+ Just rettype -> do+ body_t <- expTypeFully body'+ -- We need to turn any sizes provided by "hidden" parameter+ -- names into existential sizes instead.+ let hidden = hiddenParamNames params+ (body_t', _) <-+ unscopeType+ loc+ (filter (`elem` hidden) $ foldMap patNames params)+ body_t++ let usage = mkUsage body "return type annotation"+ onFailure (CheckingReturn rettype body_t') $+ unify usage (toStruct rettype) body_t'+ Nothing -> pure ()++ pure body'++arrayOfM ::+ SrcLoc ->+ StructType ->+ Shape Size ->+ TermTypeM StructType+arrayOfM loc t shape = do+ arrayElemType (mkUsage loc "use as array element") "type used in array" t+ pure $ arrayOf shape t
src/Language/Futhark/TypeChecker/Terms/DoLoop.hs view
@@ -21,7 +21,7 @@ import Futhark.Util.Pretty hiding (group, space) import Language.Futhark import Language.Futhark.TypeChecker.Monad hiding (BoundV)-import Language.Futhark.TypeChecker.Terms.Monad hiding (consumed)+import Language.Futhark.TypeChecker.Terms.Monad import Language.Futhark.TypeChecker.Terms.Pat import Language.Futhark.TypeChecker.Types import Language.Futhark.TypeChecker.Unify@@ -35,9 +35,9 @@ where check constraints x y = case (M.lookup x constraints, M.lookup y constraints) of- (Just (_, Size (Just (NamedSize x')) _), _) ->+ (Just (_, Size (Just (Var x' _ _)) _), _) -> check constraints (qualLeaf x') y- (_, Just (_, Size (Just (NamedSize y')) _)) ->+ (_, Just (_, Size (Just (Var y' _ _)) _)) -> check constraints x (qualLeaf y') _ -> x == y@@ -45,153 +45,55 @@ -- | Replace specified sizes with distinct fresh size variables. someDimsFreshInType :: SrcLoc ->- Rigidity -> Name -> [VName] -> TypeBase Size als -> TermTypeM (TypeBase Size als)-someDimsFreshInType loc r desc fresh t = do+someDimsFreshInType loc desc fresh t = do areSameSize <- getAreSame let freshen v = any (areSameSize v) fresh bitraverse (onDim freshen) pure t where- onDim freshen (NamedSize d)+ onDim freshen (Var d _ _) | freshen $ qualLeaf d = do- v <- newDimVar loc r desc- pure $ NamedSize $ qualName v+ v <- newFlexibleDim (mkUsage' loc) desc+ pure $ sizeFromName (qualName v) loc onDim _ d = pure d -- | Replace the specified sizes with fresh size variables of the -- specified ridigity. Returns the new fresh size variables. freshDimsInType ::- SrcLoc ->+ Usage -> Rigidity -> Name -> [VName] ->- TypeBase Size als ->- TermTypeM (TypeBase Size als, [VName])-freshDimsInType loc r desc fresh t = do+ TypeBase Size u ->+ TermTypeM (TypeBase Size u, [VName])+freshDimsInType usage r desc fresh t = do areSameSize <- getAreSame- let freshen v = any (areSameSize v) fresh- second M.elems <$> runStateT (bitraverse (onDim freshen) pure t) mempty+ second (map snd) <$> runStateT (bitraverse (onDim areSameSize) pure t) mempty where- onDim freshen (NamedSize d)- | freshen $ qualLeaf d = do- prev_subst <- gets $ M.lookup $ qualLeaf d+ onDim areSameSize (Var (QualName _ d) _ _)+ | any (areSameSize d) fresh = do+ prev_subst <- gets $ L.find (areSameSize d . fst) case prev_subst of- Just d' -> pure $ NamedSize $ qualName d'+ Just (_, d') -> pure $ sizeFromName (qualName d') $ srclocOf usage Nothing -> do- v <- lift $ newDimVar loc r desc- modify $ M.insert (qualLeaf d) v- pure $ NamedSize $ qualName v+ v <- lift $ newDimVar usage r desc+ modify ((d, v) :)+ pure $ sizeFromName (qualName v) $ srclocOf usage onDim _ d = pure d --- | Mark bindings of names in "consumed" as Unique.-uniquePat :: Names -> Pat -> Pat-uniquePat consumed = recurse- where- recurse (Wildcard (Info t) wloc) =- Wildcard (Info $ t `setUniqueness` Nonunique) wloc- recurse (PatParens p ploc) =- PatParens (recurse p) ploc- recurse (PatAttr attr p ploc) =- PatAttr attr (recurse p) ploc- recurse (Id name (Info t) iloc)- | name `S.member` consumed =- let t' = t `setUniqueness` Unique `setAliases` mempty- in Id name (Info t') iloc- | otherwise =- let t' = t `setUniqueness` Nonunique- in Id name (Info t') iloc- recurse (TuplePat pats ploc) =- TuplePat (map recurse pats) ploc- recurse (RecordPat fs ploc) =- RecordPat (map (fmap recurse) fs) ploc- recurse (PatAscription p t ploc) =- PatAscription p t ploc- recurse p@PatLit {} = p- recurse (PatConstr n t ps ploc) =- PatConstr n t (map recurse ps) ploc--convergePat :: SrcLoc -> Pat -> Names -> PatType -> Usage -> TermTypeM Pat-convergePat loop_loc pat body_cons body_t body_loc = do- let -- Make the pattern unique where needed.- pat' = uniquePat (patNames pat `S.intersection` body_cons) pat-- -- Check that the new values of consumed merge parameters do not- -- alias something bound outside the loop, AND that anything- -- returned for a unique merge parameter does not alias anything- -- else returned. We also update the aliases for the pattern.- bound_outside <- asks $ S.fromList . M.keys . scopeVtable . termScope- let combAliases t1 t2 =- case t1 of- Scalar Record {} -> t1- _ -> t1 `addAliases` (<> aliases t2)-- checkMergeReturn (Id pat_v (Info pat_v_t) patloc) t- | unique pat_v_t,- v : _ <-- S.toList $- S.map aliasVar (aliases t) `S.intersection` bound_outside =- lift . typeError loop_loc mempty $- "Return value for consuming loop parameter"- <+> dquotes (prettyName pat_v)- <+> "aliases"- <+> dquotes (prettyName v) <> "."- | otherwise = do- (cons, obs) <- get- unless (S.null $ aliases t `S.intersection` cons) $- lift . typeError loop_loc mempty $- "Return value for loop parameter"- <+> dquotes (prettyName pat_v)- <+> "aliases other consumed loop parameter."- when- ( unique pat_v_t- && not (S.null (aliases t `S.intersection` (cons <> obs)))- )- $ lift . typeError loop_loc mempty- $ "Return value for consuming loop parameter"- <+> dquotes (prettyName pat_v)- <+> "aliases previously returned value."- if unique pat_v_t- then put (cons <> aliases t, obs)- else put (cons, obs <> aliases t)-- pure $ Id pat_v (Info (combAliases pat_v_t t)) patloc- checkMergeReturn (Wildcard (Info pat_v_t) patloc) t =- pure $ Wildcard (Info (combAliases pat_v_t t)) patloc- checkMergeReturn (PatParens p _) t =- checkMergeReturn p t- checkMergeReturn (PatAscription p _ _) t =- checkMergeReturn p t- checkMergeReturn (RecordPat pfs patloc) (Scalar (Record tfs)) =- RecordPat . M.toList <$> sequence pfs' <*> pure patloc- where- pfs' = M.intersectionWith checkMergeReturn (M.fromList pfs) tfs- checkMergeReturn (TuplePat pats patloc) t- | Just ts <- isTupleRecord t =- TuplePat <$> zipWithM checkMergeReturn pats ts <*> pure patloc- checkMergeReturn p _ =- pure p-- (pat'', (pat_cons, _)) <-- runStateT (checkMergeReturn pat' body_t) (mempty, mempty)-- let body_cons' = body_cons <> S.map aliasVar pat_cons- if body_cons' == body_cons && patternType pat'' == patternType pat- then pure pat'- else convergePat loop_loc pat'' body_cons' body_t body_loc- data ArgSource = Initial | BodyResult -wellTypedLoopArg :: ArgSource -> [VName] -> Pat -> Exp -> TermTypeM ()+wellTypedLoopArg :: ArgSource -> [VName] -> Pat ParamType -> Exp -> TermTypeM () wellTypedLoopArg src sparams pat arg = do (merge_t, _) <-- freshDimsInType (srclocOf arg) Nonrigid "loop" sparams $+ freshDimsInType (mkUsage arg desc) Nonrigid "loop" sparams $ toStruct (patternType pat) arg_t <- toStruct <$> expTypeFully arg onFailure (checking merge_t arg_t) $- unify (mkUsage (srclocOf arg) desc) merge_t arg_t+ unify (mkUsage arg desc) merge_t arg_t where (checking, desc) = case src of@@ -200,16 +102,11 @@ -- | An un-checked loop. type UncheckedLoop =- (UncheckedPat, UncheckedExp, LoopFormBase NoInfo Name, UncheckedExp)+ (UncheckedPat ParamType, UncheckedExp, LoopFormBase NoInfo Name, UncheckedExp) -- | A loop that has been type-checked. type CheckedLoop =- ([VName], Pat, Exp, LoopFormBase Info VName, Exp)--loopReturnType :: Pat -> PatType -> PatType-loopReturnType pat = returnType mempty pat_t (diet pat_t)- where- pat_t = patternType pat+ ([VName], Pat ParamType, Exp, LoopFormBase Info VName, Exp) -- | Type-check a @loop@ expression, passing in a function for -- type-checking subexpressions.@@ -218,217 +115,174 @@ UncheckedLoop -> SrcLoc -> TermTypeM (CheckedLoop, AppRes)-checkDoLoop checkExp (mergepat, mergeexp, form, loopbody) loc =- sequentially (checkExp mergeexp) $ \mergeexp' _ -> do- known_before <- M.keysSet <$> getConstraints- zeroOrderType- (mkUsage (srclocOf mergeexp) "use as loop variable")- "type used as loop variable"- . toStruct- =<< expTypeFully mergeexp'+checkDoLoop checkExp (mergepat, mergeexp, form, loopbody) loc = do+ mergeexp' <- checkExp mergeexp+ known_before <- M.keysSet <$> getConstraints+ zeroOrderType+ (mkUsage mergeexp "use as loop variable")+ "type used as loop variable"+ . toStruct+ =<< expTypeFully mergeexp' - -- The handling of dimension sizes is a bit intricate, but very- -- similar to checking a function, followed by checking a call to- -- it. The overall procedure is as follows:- --- -- (1) All empty dimensions in the merge pattern are instantiated- -- with nonrigid size variables. All explicitly specified- -- dimensions are preserved.- --- -- (2) The body of the loop is type-checked. The result type is- -- combined with the merge pattern type to determine which sizes are- -- variant, and these are turned into size parameters for the merge- -- pattern.- --- -- (3) We now conceptually have a function parameter type and- -- return type. We check that it can be called with the body type- -- as argument.- --- -- (4) Similarly to (3), we check that the "function" can be- -- called with the initial merge values as argument. The result- -- of this is the type of the loop as a whole.- --- -- (There is also a convergence loop for inferring uniqueness, but- -- that's orthogonal to the size handling.)+ -- The handling of dimension sizes is a bit intricate, but very+ -- similar to checking a function, followed by checking a call to+ -- it. The overall procedure is as follows:+ --+ -- (1) All empty dimensions in the merge pattern are instantiated+ -- with nonrigid size variables. All explicitly specified+ -- dimensions are preserved.+ --+ -- (2) The body of the loop is type-checked. The result type is+ -- combined with the merge pattern type to determine which sizes are+ -- variant, and these are turned into size parameters for the merge+ -- pattern.+ --+ -- (3) We now conceptually have a function parameter type and+ -- return type. We check that it can be called with the body type+ -- as argument.+ --+ -- (4) Similarly to (3), we check that the "function" can be+ -- called with the initial merge values as argument. The result+ -- of this is the type of the loop as a whole. - -- We don't want the loop parameters to alias their initial- -- values, so we blank them here. We will actually check them- -- properly later.- (merge_t, new_dims_map) <-- -- dim handling (1)- allDimsFreshInType loc Nonrigid "loop_d" . flip setAliases mempty- =<< expTypeFully mergeexp'- let new_dims_to_initial_dim = M.toList new_dims_map- new_dims = map fst new_dims_to_initial_dim+ (merge_t, new_dims_map) <-+ -- dim handling (1)+ allDimsFreshInType (mkUsage loc "loop parameter type inference") Nonrigid "loop_d"+ =<< expTypeFully mergeexp'+ let new_dims_to_initial_dim = M.toList new_dims_map+ new_dims = map fst new_dims_to_initial_dim - -- dim handling (2)- let checkLoopReturnSize mergepat' loopbody' = do- loopbody_t <- expTypeFully loopbody'- pat_t <-- someDimsFreshInType loc Nonrigid "loop" new_dims- =<< normTypeFully (patternType mergepat')+ -- dim handling (2)+ let checkLoopReturnSize mergepat' loopbody' = do+ loopbody_t <- expTypeFully loopbody'+ pat_t <-+ someDimsFreshInType loc "loop" new_dims+ =<< normTypeFully (patternType mergepat') - -- We are ignoring the dimensions here, because any mismatches- -- should be turned into fresh size variables.- onFailure (CheckingLoopBody (toStruct pat_t) (toStruct loopbody_t)) $- unify- (mkUsage (srclocOf loopbody) "matching loop body to loop pattern")- (toStruct pat_t)- (toStruct loopbody_t)+ -- We are ignoring the dimensions here, because any mismatches+ -- should be turned into fresh size variables.+ onFailure (CheckingLoopBody (toStruct pat_t) (toStruct loopbody_t)) $+ unify+ (mkUsage loopbody "matching loop body to loop pattern")+ (toStruct pat_t)+ (toStruct loopbody_t) - -- Figure out which of the 'new_dims' dimensions are variant.- -- This works because we know that each dimension from- -- new_dims in the pattern is unique and distinct.- areSameSize <- getAreSame- let onDims _ x y- | x == y = pure x- onDims _ (NamedSize v) d- | Just (v', d') <-- L.find (areSameSize (qualLeaf v) . fst) new_dims_to_initial_dim = do- if d' == d- then modify $ first $ M.insert v' (SizeSubst d)+ -- Figure out which of the 'new_dims' dimensions are variant.+ -- This works because we know that each dimension from+ -- new_dims in the pattern is unique and distinct.+ areSameSize <- getAreSame+ let onDims _ x y+ | x == y = pure x+ onDims _ e d = do+ forM_ (fvVars $ freeInExp e) $ \v -> do+ case L.find (areSameSize v . fst) new_dims_to_initial_dim of+ Just (_, e') ->+ if e' == d+ then modify $ first $ M.insert v $ ExpSubst e' else- unless (qualLeaf v `S.member` known_before) $- modify (second (qualLeaf v :))- pure $ NamedSize v- onDims _ x _ = pure x- loopbody_t' <- normTypeFully loopbody_t- merge_t' <- normTypeFully merge_t+ unless (v `S.member` known_before) $+ modify (second (v :))+ _ ->+ pure ()+ pure e+ loopbody_t' <- normTypeFully loopbody_t+ merge_t' <- normTypeFully merge_t - let (init_substs, sparams) =- execState (matchDims onDims merge_t' loopbody_t') mempty+ let (init_substs, sparams) =+ execState (matchDims onDims merge_t' loopbody_t') mempty - -- Make sure that any of new_dims that are invariant will be- -- replaced with the invariant size in the loop body. Failure- -- to do this can cause type annotations to still refer to- -- new_dims.- let dimToInit (v, SizeSubst d) =- constrain v $ Size (Just d) (mkUsage loc "size of loop parameter")- dimToInit _ =- pure ()- mapM_ dimToInit $ M.toList init_substs+ -- Make sure that any of new_dims that are invariant will be+ -- replaced with the invariant size in the loop body. Failure+ -- to do this can cause type annotations to still refer to+ -- new_dims.+ let dimToInit (v, ExpSubst e) =+ constrain v $ Size (Just e) (mkUsage loc "size of loop parameter")+ dimToInit _ =+ pure ()+ mapM_ dimToInit $ M.toList init_substs - mergepat'' <- applySubst (`M.lookup` init_substs) <$> updateTypes mergepat'+ mergepat'' <- applySubst (`M.lookup` init_substs) <$> updateTypes mergepat' - -- Eliminate those new_dims that turned into sparams so it won't- -- look like we have ambiguous sizes lying around.- modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` sparams+ -- Eliminate those new_dims that turned into sparams so it won't+ -- look like we have ambiguous sizes lying around.+ modifyConstraints $ M.filterWithKey $ \k _ -> k `notElem` sparams - -- dim handling (3)- --- -- The only trick here is that we have to turn any instances- -- of loop parameters in the type of loopbody' rigid,- -- because we are no longer in a position to change them,- -- really.- wellTypedLoopArg BodyResult sparams mergepat'' loopbody'+ -- dim handling (3)+ --+ -- The only trick here is that we have to turn any instances+ -- of loop parameters in the type of loopbody' rigid,+ -- because we are no longer in a position to change them,+ -- really.+ wellTypedLoopArg BodyResult sparams mergepat'' loopbody' - pure (nubOrd sparams, mergepat'')+ pure (nubOrd sparams, mergepat'') - -- First we do a basic check of the loop body to figure out which of- -- the merge parameters are being consumed. For this, we first need- -- to check the merge pattern, which requires the (initial) merge- -- expression.- --- -- Play a little with occurences to ensure it does not look like- -- none of the merge variables are being used.- ((sparams, mergepat', form', loopbody'), bodyflow) <-- case form of- For i uboundexp -> do- uboundexp' <-- require "being the bound in a 'for' loop" anySignedType- =<< checkExp uboundexp- bound_t <- expTypeFully uboundexp'- bindingIdent i bound_t $ \i' ->- noUnique . bindingPat [] mergepat (Ascribed merge_t) $- \mergepat' -> tapOccurrences $ incLevel $ do- loopbody' <- noSizeEscape $ checkExp loopbody- (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'- pure- ( sparams,- mergepat'',- For i' uboundexp',- loopbody'- )- ForIn xpat e -> do- (arr_t, _) <- newArrayType (srclocOf e) "e" 1- e' <- unifies "being iterated in a 'for-in' loop" arr_t =<< checkExp e- t <- expTypeFully e'- case t of- _- | Just t' <- peelArray 1 t ->- bindingPat [] xpat (Ascribed t') $ \xpat' ->- noUnique . bindingPat [] mergepat (Ascribed merge_t) $- \mergepat' -> tapOccurrences $ incLevel $ do- loopbody' <- noSizeEscape $ checkExp loopbody- (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'- pure- ( sparams,- mergepat'',- ForIn xpat' e',- loopbody'- )- | otherwise ->- typeError (srclocOf e) mempty $- "Iteratee of a for-in loop must be an array, but expression has type"- <+> pretty t- While cond ->- noUnique . bindingPat [] mergepat (Ascribed merge_t) $ \mergepat' ->- tapOccurrences- . incLevel- . sequentially- ( checkExp cond- >>= unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)+ (sparams, mergepat', form', loopbody') <-+ case form of+ For i uboundexp -> do+ uboundexp' <-+ require "being the bound in a 'for' loop" anySignedType+ =<< checkExp uboundexp+ bound_t <- expTypeFully uboundexp'+ bindingIdent i bound_t $ \i' ->+ bindingPat [] mergepat merge_t $+ \mergepat' -> incLevel $ do+ loopbody' <- checkExp loopbody+ (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+ pure+ ( sparams,+ mergepat'',+ For i' uboundexp',+ loopbody' )- $ \cond' _ -> do- loopbody' <- noSizeEscape $ checkExp loopbody- (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'- pure- ( sparams,- mergepat'',- While cond',- loopbody'- )-- mergepat'' <- do- loopbody_t <- expTypeFully loopbody'- convergePat loc mergepat' (allConsumed bodyflow) loopbody_t $- mkUsage (srclocOf loopbody') "being (part of) the result of the loop body"-- merge_t' <- expTypeFully mergeexp'- let consumeMerge (Id _ (Info pt) ploc) mt- | unique pt = consume ploc $ aliases mt- consumeMerge (TuplePat pats _) t- | Just ts <- isTupleRecord t =- zipWithM_ consumeMerge pats ts- consumeMerge (PatParens pat _) t =- consumeMerge pat t- consumeMerge (PatAscription pat _ _) t =- consumeMerge pat t- consumeMerge _ _ =- pure ()- consumeMerge mergepat'' merge_t'-- -- dim handling (4)- wellTypedLoopArg Initial sparams mergepat'' mergeexp'+ ForIn xpat e -> do+ (arr_t, _) <- newArrayType (mkUsage' (srclocOf e)) "e" 1+ e' <- unifies "being iterated in a 'for-in' loop" arr_t =<< checkExp e+ t <- expTypeFully e'+ case t of+ _+ | Just t' <- peelArray 1 t ->+ bindingPat [] xpat t' $ \xpat' ->+ bindingPat [] mergepat merge_t $+ \mergepat' -> incLevel $ do+ loopbody' <- checkExp loopbody+ (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+ pure+ ( sparams,+ mergepat'',+ ForIn (fmap toStruct xpat') e',+ loopbody'+ )+ | otherwise ->+ typeError (srclocOf e) mempty $+ "Iteratee of a for-in loop must be an array, but expression has type"+ <+> pretty t+ While cond ->+ bindingPat [] mergepat merge_t $ \mergepat' ->+ incLevel $ do+ cond' <-+ checkExp cond+ >>= unifies "being the condition of a 'while' loop" (Scalar $ Prim Bool)+ loopbody' <- checkExp loopbody+ (sparams, mergepat'') <- checkLoopReturnSize mergepat' loopbody'+ pure+ ( sparams,+ mergepat'',+ While cond',+ loopbody'+ ) - (loopt, retext) <-- freshDimsInType loc (Rigid RigidLoop) "loop" sparams $- loopReturnType mergepat'' merge_t'- -- We set all of the uniqueness to be unique. This is intentional,- -- and matches what happens for function calls. Those arrays that- -- really *cannot* be consumed will alias something unconsumable,- -- and will be caught that way.- let bound_here = patNames mergepat'' <> S.fromList sparams <> form_bound- form_bound =- case form' of- For v _ -> S.singleton $ identName v- ForIn forpat _ -> patNames forpat- While {} -> mempty- loopt' =- second (`S.difference` S.map AliasBound bound_here) $- loopt `setUniqueness` Unique+ -- dim handling (4)+ wellTypedLoopArg Initial sparams mergepat' mergeexp' - pure- ( (sparams, mergepat'', mergeexp', form', loopbody'),- AppRes loopt' retext- )+ (loopt, retext) <-+ freshDimsInType+ (mkUsage loc "inference of loop result type")+ (Rigid RigidLoop)+ "loop"+ sparams+ (patternType mergepat')+ pure+ ( (sparams, mergepat', mergeexp', form', loopbody'),+ AppRes (toStruct loopt) retext+ )
src/Language/Futhark/TypeChecker/Terms/Monad.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE Strict #-}+ -- | Facilities for type-checking terms. Factored out of -- "Language.Futhark.TypeChecker.Terms" to prevent the module from -- being gigantic.@@ -7,12 +9,9 @@ module Language.Futhark.TypeChecker.Terms.Monad ( TermTypeM, runTermTypeM,- liftTypeM, ValBinding (..),- Locality (..), SizeSource (SourceBound, SourceSlice),- NameReason (..),- InferredType (..),+ Inferred (..), Checking (..), withEnv, localScope,@@ -27,262 +26,74 @@ newArrayType, allDimsFreshInType, updateTypes,+ Names, -- * Primitive checking unifies, require, checkTypeExpNonrigid,- checkTypeExpRigid, -- * Sizes isInt64,- maybeDimFromExp,- dimFromExp,- sizeFromArg,- noSizeEscape, -- * Control flow- collectOccurrences,- tapOccurrences,- alternative,- sequentially, incLevel, - -- * Consumption and uniqueness- Names,- Occurrence (..),- Occurrences,- noUnique,- removeSeminullOccurrences,- occur,- observe,- consume,- consuming,- observation,- consumption,- checkIfConsumable,- seqOccurrences,- checkOccurrences,- allConsumed,- -- * Errors unusedSize,- uniqueReturnAliased,- returnAliased,- badLetWithValue,- anyConsumption,- allOccurring, ) where import Control.Monad import Control.Monad.Except import Control.Monad.Reader-import Control.Monad.State-import Data.Bifunctor+import Control.Monad.State.Strict import Data.Bitraversable import Data.Char (isAscii)-import Data.List (find, isPrefixOf, sort) import Data.Map.Strict qualified as M import Data.Maybe import Data.Set qualified as S import Data.Text qualified as T+import Futhark.FreshNames hiding (newName)+import Futhark.FreshNames qualified import Futhark.Util.Pretty hiding (space) import Language.Futhark import Language.Futhark.Semantic (includeToFilePath) import Language.Futhark.Traversals-import Language.Futhark.TypeChecker.Monad hiding (BoundV)+import Language.Futhark.TypeChecker.Monad hiding (BoundV, stateNameSource) import Language.Futhark.TypeChecker.Monad qualified as TypeM import Language.Futhark.TypeChecker.Types-import Language.Futhark.TypeChecker.Unify hiding (Usage)+import Language.Futhark.TypeChecker.Unify import Prelude hiding (mod) ---- Uniqueness--data Usage- = Consumed SrcLoc- | Observed SrcLoc- deriving (Eq, Ord, Show)- type Names = S.Set VName --- | The consumption set is a Maybe so we can distinguish whether a--- consumption took place, but the variable went out of scope since,--- or no consumption at all took place.-data Occurrence = Occurrence- { observed :: Names,- consumed :: Maybe Names,- location :: SrcLoc- }- deriving (Eq, Show)--instance Located Occurrence where- locOf = locOf . location--observation :: Aliasing -> SrcLoc -> Occurrence-observation = flip Occurrence Nothing . S.map aliasVar--consumption :: Aliasing -> SrcLoc -> Occurrence-consumption = Occurrence S.empty . Just . S.map aliasVar---- | A null occurence is one that we can remove without affecting--- anything.-nullOccurrence :: Occurrence -> Bool-nullOccurrence occ = S.null (observed occ) && isNothing (consumed occ)---- | A seminull occurence is one that does not contain references to--- any variables in scope. The big difference is that a seminull--- occurence may denote a consumption, as long as the array that was--- consumed is now out of scope.-seminullOccurrence :: Occurrence -> Bool-seminullOccurrence occ = S.null (observed occ) && maybe True S.null (consumed occ)--type Occurrences = [Occurrence]--type UsageMap = M.Map VName [Usage]--usageMap :: Occurrences -> UsageMap-usageMap = foldl comb M.empty- where- comb m (Occurrence obs cons loc) =- let m' = S.foldl' (ins $ Observed loc) m obs- in S.foldl' (ins $ Consumed loc) m' $ fromMaybe mempty cons- ins v m k = M.insertWith (++) k [v] m--combineOccurrences :: VName -> Usage -> Usage -> TermTypeM Usage-combineOccurrences _ (Observed loc) (Observed _) = pure $ Observed loc-combineOccurrences name (Consumed wloc) (Observed rloc) =- useAfterConsume name rloc wloc-combineOccurrences name (Observed rloc) (Consumed wloc) =- useAfterConsume name rloc wloc-combineOccurrences name (Consumed loc1) (Consumed loc2) =- useAfterConsume name (max loc1 loc2) (min loc1 loc2)--checkOccurrences :: Occurrences -> TermTypeM ()-checkOccurrences = void . M.traverseWithKey comb . usageMap- where- comb _ [] = pure ()- comb name (u : us) = foldM_ (combineOccurrences name) u us--allObserved :: Occurrences -> Names-allObserved = S.unions . map observed--allConsumed :: Occurrences -> Names-allConsumed = S.unions . map (fromMaybe mempty . consumed)--allOccurring :: Occurrences -> Names-allOccurring occs = allConsumed occs <> allObserved occs---- | Find any consumption that references a variable in scope.-anyConsumption :: Occurrences -> Maybe Occurrence-anyConsumption = find (maybe False (not . null) . consumed)--seqOccurrences :: Occurrences -> Occurrences -> Occurrences-seqOccurrences occurs1 occurs2 =- filter (not . nullOccurrence) $ map filt occurs1 ++ occurs2- where- filt occ =- occ {observed = observed occ `S.difference` postcons}- postcons = allConsumed occurs2--altOccurrences :: Occurrences -> Occurrences -> Occurrences-altOccurrences occurs1 occurs2 =- filter (not . nullOccurrence) $ map filt1 occurs1 ++ map filt2 occurs2- where- filt1 occ =- occ- { consumed = S.difference <$> consumed occ <*> pure cons2,- observed = observed occ `S.difference` cons2- }- filt2 occ =- occ- { consumed = consumed occ,- observed = observed occ `S.difference` cons1- }- cons1 = allConsumed occurs1- cons2 = allConsumed occurs2---- | How something was bound.-data Locality- = -- | In the current function- Local- | -- | In an enclosing function, but not the current one.- Nonlocal- | -- | At global scope.- Global- deriving (Show, Eq, Ord)- data ValBinding- = -- | Aliases in parameters indicate the lexical- -- closure.- BoundV Locality [TypeParam] PatType+ = BoundV [TypeParam] StructType | OverloadedF [PrimType] [Maybe PrimType] (Maybe PrimType) | EqualityF- | WasConsumed SrcLoc deriving (Show) ---- Errors--describeVar :: SrcLoc -> VName -> TermTypeM (Doc a)-describeVar loc v =- gets $- maybe ("variable" <+> dquotes (prettyName v)) (nameReason loc)- . M.lookup v- . stateNames--useAfterConsume :: VName -> SrcLoc -> SrcLoc -> TermTypeM a-useAfterConsume name rloc wloc = do- name' <- describeVar rloc name- typeError rloc mempty . withIndexLink "use-after-consume" $- "Using"- <+> name' <> ", but this was consumed at"- <+> pretty (locStrRel rloc wloc) <> ". (Possibly through aliasing.)"--badLetWithValue :: (Pretty arr, Pretty src) => arr -> src -> SrcLoc -> TermTypeM a-badLetWithValue arre vale loc =- typeError loc mempty $- "Source array for in-place update"- </> indent 2 (pretty arre)- </> "might alias update value"- </> indent 2 (pretty vale)- </> "Hint: use"- <+> dquotes "copy"- <+> "to remove aliases from the value."--returnAliased :: Name -> SrcLoc -> TermTypeM ()-returnAliased name loc =- typeError loc mempty . withIndexLink "return-aliased" $- "Unique-typed return value is aliased to"- <+> dquotes (prettyName name) <> ", which is not consumable."--uniqueReturnAliased :: SrcLoc -> TermTypeM a-uniqueReturnAliased loc =- typeError loc mempty . withIndexLink "unique-return-aliased" $- "A unique-typed component of the return value is aliased to some other component."--notConsumable :: MonadTypeChecker m => SrcLoc -> Doc () -> m b-notConsumable loc v =- typeError loc mempty . withIndexLink "not-consumable" $- "Would consume" <+> v <> ", which is not consumable."- unusedSize :: (MonadTypeChecker m) => SizeBinder VName -> m a unusedSize p = typeError p mempty . withIndexLink "unused-size" $ "Size" <+> pretty p <+> "unused in pattern." ---- Scope management--data InferredType+data Inferred t = NoneInferred- | Ascribed PatType+ | Ascribed t +instance Functor Inferred where+ fmap _ NoneInferred = NoneInferred+ fmap f (Ascribed t) = Ascribed (f t)+ data Checking = CheckingApply (Maybe (QualName VName)) Exp StructType StructType- | CheckingReturn StructType StructType+ | CheckingReturn ResType StructType | CheckingAscription StructType StructType | CheckingLetGeneralise Name | CheckingParams (Maybe Name)- | CheckingPat UncheckedPat InferredType+ | CheckingPat (UncheckedPat StructType) (Inferred StructType) | CheckingLoopBody StructType StructType | CheckingLoopInitial StructType StructType | CheckingRecordUpdate [Name] StructType StructType@@ -328,9 +139,9 @@ pretty (CheckingPat pat NoneInferred) = "Invalid pattern" <+> dquotes (pretty pat) <> "." pretty (CheckingPat pat (Ascribed t)) =- "Pat"- <+> dquotes (pretty pat)- <+> "cannot match value of type"+ "Pattern"+ </> indent 2 (pretty pat)+ </> "cannot match value of type" </> indent 2 (pretty t) pretty (CheckingLoopBody expected actual) = "Loop body does not have expected type."@@ -378,7 +189,10 @@ data TermEnv = TermEnv { termScope :: TermScope, termChecking :: Maybe Checking,- termLevel :: Level+ termLevel :: Level,+ termChecker :: UncheckedExp -> TermTypeM Exp,+ termOuterEnv :: Env,+ termImportName :: ImportName } data TermScope = TermScope@@ -402,14 +216,8 @@ scopeModTable = envModTable env } where- vtable = M.mapWithKey valBinding $ envVtable env- valBinding k (TypeM.BoundV tps v) =- BoundV Global tps $ selfAliasing (S.singleton (AliasBound k)) v- -- FIXME: hack, #1675- selfAliasing als (Scalar (Record ts)) =- Scalar $ Record $ M.map (selfAliasing als) ts- selfAliasing als t =- t `setAliases` (if arrayRank t > 0 then als else mempty)+ vtable = M.map valBinding $ envVtable env+ valBinding (TypeM.BoundV tps v) = BoundV tps v withEnv :: TermEnv -> Env -> TermEnv withEnv tenv env = tenv {termScope = termScope tenv <> envToTermScope env}@@ -437,50 +245,42 @@ (Maybe (ExpBase NoInfo VName)) deriving (Eq, Ord, Show) --- | A description of where an artificial compiler-generated--- intermediate name came from.-data NameReason- = -- | Name is the result of a function application.- NameAppRes (Maybe (QualName VName)) SrcLoc--nameReason :: SrcLoc -> NameReason -> Doc a-nameReason loc (NameAppRes Nothing apploc) =- "result of application at" <+> pretty (locStrRel loc apploc)-nameReason loc (NameAppRes fname apploc) =- "result of applying"- <+> dquotes (pretty fname)- <+> parens ("at" <+> pretty (locStrRel loc apploc))- -- | The state is a set of constraints and a counter for generating -- type names. This is distinct from the usual counter we use for -- generating unique names, as these will be user-visible. data TermTypeState = TermTypeState { stateConstraints :: Constraints, stateCounter :: !Int,- -- | Mapping function arguments encountered to- -- the sizes they ended up generating (when- -- they could not be substituted directly).- -- This happens for function arguments that are- -- not constants or names.- stateDimTable :: M.Map SizeSource VName,- stateNames :: M.Map VName NameReason,- stateOccs :: Occurrences+ stateUsed :: S.Set VName,+ stateWarnings :: Warnings,+ stateNameSource :: VNameSource } newtype TermTypeM a- = TermTypeM (ReaderT TermEnv (StateT TermTypeState TypeM) a)+ = TermTypeM+ ( ReaderT+ TermEnv+ (StateT TermTypeState (Except (Warnings, TypeError)))+ a+ ) deriving ( Monad, Functor, Applicative, MonadReader TermEnv,- MonadState TermTypeState,- MonadError TypeError+ MonadState TermTypeState ) -liftTypeM :: TypeM a -> TermTypeM a-liftTypeM = TermTypeM . lift . lift+instance MonadError TypeError TermTypeM where+ throwError e = TermTypeM $ do+ ws <- gets stateWarnings+ throwError (ws, e) + catchError (TermTypeM m) f =+ TermTypeM $ m `catchError` f'+ where+ f' (_, e) = let TermTypeM m' = f e in m'+ incCounter :: TermTypeM Int incCounter = do s <- get@@ -500,15 +300,15 @@ i <- incCounter v <- newID $ mkTypeVarName desc i constrain v $ NoConstraint Lifted $ mkUsage' loc- pure $ Scalar $ TypeVar mempty Nonunique (qualName v) []+ pure $ Scalar $ TypeVar mempty (qualName v) [] curLevel = asks termLevel - newDimVar loc rigidity name = do+ newDimVar usage rigidity name = do dim <- newTypeName name case rigidity of- Rigid rsrc -> constrain dim $ UnknowableSize loc rsrc- Nonrigid -> constrain dim $ Size Nothing $ mkUsage' loc+ Rigid rsrc -> constrain dim $ UnknownSize (srclocOf usage) rsrc+ Nonrigid -> constrain dim $ Size Nothing usage pure dim unifyError loc notes bcs doc = do@@ -550,8 +350,8 @@ QualName VName -> SrcLoc -> [TypeParam] ->- PatType ->- TermTypeM ([VName], PatType)+ StructType ->+ TermTypeM ([VName], StructType) instantiateTypeScheme qn loc tparams t = do let tnames = map typeParamName tparams (tparam_names, tparam_substs) <- mapAndUnzipM (instantiateTypeParam qn loc) tparams@@ -575,11 +375,11 @@ TypeParamType x _ _ -> do constrain v . NoConstraint x . mkUsage loc . docText $ "instantiated type parameter of " <> dquotes (pretty qn)- pure (v, Subst [] $ RetType [] $ Scalar $ TypeVar mempty Nonunique (qualName v) [])+ pure (v, Subst [] $ RetType [] $ Scalar $ TypeVar mempty (qualName v) []) TypeParamDim {} -> do constrain v . Size Nothing . mkUsage loc . docText $ "instantiated size parameter of " <> dquotes (pretty qn)- pure (v, SizeSubst $ NamedSize $ qualName v)+ pure (v, ExpSubst $ sizeFromName (qualName v) loc) checkQualNameWithEnv :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName) checkQualNameWithEnv space qn@(QualName quals name) loc = do@@ -610,7 +410,7 @@ checkIntrinsic :: Namespace -> QualName Name -> SrcLoc -> TermTypeM (TermScope, QualName VName) checkIntrinsic space qn@(QualName _ name) loc | Just v <- M.lookup (space, name) intrinsicsNameMap = do- me <- liftTypeM askImportName+ me <- asks termImportName unless (isBuiltin (includeToFilePath me)) $ warn loc "Using intrinsic functions directly can easily crash the compiler or result in wrong code generation." scope <- asks termScope@@ -622,10 +422,26 @@ localScope f = local $ \tenv -> tenv {termScope = f $ termScope tenv} instance MonadTypeChecker TermTypeM where- warn loc problem = liftTypeM $ warn loc problem- newName = liftTypeM . newName- newID = liftTypeM . newID+ checkExpForSize e = do+ checker <- asks termChecker+ e' <- checker e+ let t = toStruct $ typeOf e'+ unify (mkUsage (srclocOf e') "Size expression") t (Scalar (Prim (Signed Int64)))+ updateTypes e' + warnings ws =+ modify $ \s -> s {stateWarnings = stateWarnings s <> ws}++ warn loc problem = warnings $ singleWarning (srclocOf loc) problem++ newName v = do+ s <- get+ let (v', src') = Futhark.FreshNames.newName (stateNameSource s) v+ put $ s {stateNameSource = src'}+ pure v'++ newID s = newName $ VName s 0+ newTypeName name = do i <- incCounter newID $ mkTypeVarName name i@@ -636,12 +452,10 @@ scope {scopeNameMap = m <> scopeNameMap scope} bindVal v (TypeM.BoundV tps t) = localScope $ \scope ->- scope {scopeVtable = M.insert v vb $ scopeVtable scope}- where- vb = BoundV Local tps $ fromStruct t+ scope {scopeVtable = M.insert v (BoundV tps t) $ scopeVtable scope} lookupType loc qn = do- outer_env <- liftTypeM askEnv+ outer_env <- asks termOuterEnv (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Type qn loc case M.lookup name $ scopeTypeTable scope of Nothing -> unknownType loc qn@@ -660,7 +474,6 @@ Just m -> pure (qn', m) lookupVar loc qn = do- outer_env <- liftTypeM askEnv (scope, qn'@(QualName qs name)) <- checkQualNameWithEnv Term qn loc let usage = mkUsage loc $ docText $ "use of " <> dquotes (pretty qn) @@ -668,11 +481,16 @@ Nothing -> typeError loc mempty $ "Unknown variable" <+> dquotes (pretty qn) <> "."- Just (WasConsumed wloc) -> useAfterConsume name loc wloc- Just (BoundV _ tparams t)- | "_" `isPrefixOf` baseString name -> underscoreUse loc qn- | otherwise -> do+ Just (BoundV tparams t) -> do+ when (null qs) . modify $ \s ->+ s {stateUsed = S.insert (qualLeaf qn') $ stateUsed s}+ when (T.head (nameToText (baseName name)) == '_') $+ underscoreUse loc qn+ if null tparams && null qs+ then pure t+ else do (tnames, t') <- instantiateTypeScheme qn' loc tparams t+ outer_env <- asks termOuterEnv pure $ qualifyTypeVars outer_env tnames qs t' Just EqualityF -> do argtype <- newTypeVar loc "t"@@ -688,10 +506,8 @@ argtype <- newTypeVar loc "t" mustBeOneOf ts usage argtype let (pts', rt') = instOverloaded argtype pts rt- arrow xt yt = Scalar $ Arrow mempty Unnamed Observe xt $ RetType [] yt- pure $ fromStruct $ foldr arrow rt' pts'+ pure $ foldFunType (map (toParam Observe) pts') $ RetType [] $ toRes Nonunique rt' - observe $ Ident name (Info t) loc pure (qn', t) where instOverloaded argtype pts rt =@@ -699,15 +515,6 @@ maybe (toStruct argtype) (Scalar . Prim) rt ) - checkNamedSize loc v = do- (v', t) <- lookupVar loc v- onFailure (CheckingRequired [Scalar $ Prim $ Signed Int64] (toStruct t)) $- unify (mkUsage loc "use as array size") (toStruct t) $- Scalar $- Prim $- Signed Int64- pure v'- typeError loc notes s = do checking <- asks termChecking case checking of@@ -721,27 +528,18 @@ extSize :: SrcLoc -> SizeSource -> TermTypeM (Size, Maybe VName) extSize loc e = do- prev <- gets $ M.lookup e . stateDimTable- case prev of- Nothing -> do- let rsrc = case e of- SourceArg (FName fname) e' ->- RigidArg fname $ prettyTextOneLine e'- SourceBound e' ->- RigidBound $ prettyTextOneLine e'- SourceSlice d i j s ->- RigidSlice d $ prettyTextOneLine $ DimSlice i j s- d <- newDimVar loc (Rigid rsrc) "n"- modify $ \s -> s {stateDimTable = M.insert e d $ stateDimTable s}- pure- ( NamedSize $ qualName d,- Just d- )- Just d ->- pure- ( NamedSize $ qualName d,- Just d- )+ let rsrc = case e of+ SourceArg (FName fname) e' ->+ RigidArg fname $ prettyTextOneLine e'+ SourceBound e' ->+ RigidBound $ prettyTextOneLine e'+ SourceSlice d i j s ->+ RigidSlice d $ prettyTextOneLine $ DimSlice i j s+ d <- newRigidDim loc rsrc "n"+ pure+ ( sizeFromName (qualName d) loc,+ Just d+ ) incLevel :: TermTypeM a -> TermTypeM a incLevel = local $ \env -> env {termLevel = termLevel env + 1}@@ -749,41 +547,42 @@ -- | Get the type of an expression, with top level type variables -- substituted. Never call 'typeOf' directly (except in a few -- carefully inspected locations)!-expType :: Exp -> TermTypeM PatType-expType = normPatType . typeOf+expType :: Exp -> TermTypeM StructType+expType = normType . typeOf -- | Get the type of an expression, with all type variables -- substituted. Slower than 'expType', but sometimes necessary. -- Never call 'typeOf' directly (except in a few carefully inspected -- locations)!-expTypeFully :: Exp -> TermTypeM PatType+expTypeFully :: Exp -> TermTypeM StructType expTypeFully = normTypeFully . typeOf -newArrayType :: SrcLoc -> Name -> Int -> TermTypeM (StructType, StructType)-newArrayType loc desc r = do+newArrayType :: Usage -> Name -> Int -> TermTypeM (StructType, StructType)+newArrayType usage desc r = do v <- newTypeName desc- constrain v $ NoConstraint Unlifted $ mkUsage' loc- dims <- replicateM r $ newDimVar loc Nonrigid "dim"- let rowt = TypeVar () Nonunique (qualName v) []+ constrain v $ NoConstraint Unlifted usage+ dims <- replicateM r $ newDimVar usage Nonrigid "dim"+ let rowt = TypeVar mempty (qualName v) []+ mkSize = flip sizeFromName (srclocOf usage) . qualName pure- ( Array () Nonunique (Shape $ map (NamedSize . qualName) dims) rowt,+ ( Array mempty (Shape $ map mkSize dims) rowt, Scalar rowt ) -- | Replace *all* dimensions with distinct fresh size variables. allDimsFreshInType ::- SrcLoc ->+ Usage -> Rigidity -> Name -> TypeBase Size als -> TermTypeM (TypeBase Size als, M.Map VName Size)-allDimsFreshInType loc r desc t =+allDimsFreshInType usage r desc t = runStateT (bitraverse onDim pure t) mempty where onDim d = do- v <- lift $ newDimVar loc r desc+ v <- lift $ newDimVar usage r desc modify $ M.insert v d- pure $ NamedSize $ qualName v+ pure $ sizeFromName (qualName v) $ srclocOf usage -- | Replace all type variables with their concrete types. updateTypes :: ASTMappable e => e -> TermTypeM e@@ -794,9 +593,8 @@ { mapOnExp = astMap tv, mapOnName = pure, mapOnStructType = normTypeFully,- mapOnPatType = normTypeFully,- mapOnStructRetType = normTypeFully,- mapOnPatRetType = normTypeFully+ mapOnParamType = normTypeFully,+ mapOnResRetType = normTypeFully } --- Basic checking@@ -815,7 +613,7 @@ termCheckTypeExp :: TypeExp NoInfo Name ->- TermTypeM (TypeExp Info VName, [VName], StructRetType)+ TermTypeM (TypeExp Info VName, [VName], ResRetType) termCheckTypeExp te = do (te', svars, rettype, _l) <- checkTypeExp te @@ -825,172 +623,24 @@ -- where we actually turn these into size variables? RetType dims st <- renameRetType rettype - -- Observe the sizes so we do not get any warnings about them not- -- being used.- mapM_ observeDim $ freeInType st pure (te', svars, RetType dims st)- where- observeDim v =- observe $ Ident v (Info $ Scalar $ Prim $ Signed Int64) mempty -checkTypeExpNonrigid :: TypeExp NoInfo Name -> TermTypeM (TypeExp Info VName, StructType, [VName])+checkTypeExpNonrigid :: TypeExp NoInfo Name -> TermTypeM (TypeExp Info VName, ResType, [VName]) checkTypeExpNonrigid te = do (te', svars, RetType dims st) <- termCheckTypeExp te forM_ (svars ++ dims) $ \v -> constrain v $ Size Nothing $ mkUsage (srclocOf te) "anonymous size in type expression" pure (te', st, svars ++ dims) -checkTypeExpRigid ::- TypeExp NoInfo Name ->- RigidSource ->- TermTypeM (TypeExp Info VName, StructType, [VName])-checkTypeExpRigid te rsrc = do- (te', svars, RetType dims st) <- termCheckTypeExp te- forM_ (svars ++ dims) $ \v ->- constrain v $ UnknowableSize (srclocOf te) rsrc- pure (te', st, svars ++ dims)- --- Sizes isInt64 :: Exp -> Maybe Int64 isInt64 (Literal (SignedValue (Int64Value k')) _) = Just $ fromIntegral k' isInt64 (IntLit k' _ _) = Just $ fromInteger k' isInt64 (Negate x _) = negate <$> isInt64 x+isInt64 (Parens x _) = isInt64 x isInt64 _ = Nothing -maybeDimFromExp :: Exp -> Maybe Size-maybeDimFromExp (Var v _ _) = Just $ NamedSize v-maybeDimFromExp (Parens e _) = maybeDimFromExp e-maybeDimFromExp (QualParens _ e _) = maybeDimFromExp e-maybeDimFromExp e = ConstSize . fromIntegral <$> isInt64 e--dimFromExp :: (Exp -> SizeSource) -> Exp -> TermTypeM (Size, Maybe VName)-dimFromExp rf (Attr _ e _) = dimFromExp rf e-dimFromExp rf (Assert _ e _ _) = dimFromExp rf e-dimFromExp rf (Parens e _) = dimFromExp rf e-dimFromExp rf (QualParens _ e _) = dimFromExp rf e-dimFromExp rf e- | Just d <- maybeDimFromExp e =- pure (d, Nothing)- | otherwise =- extSize (srclocOf e) $ rf e--sizeFromArg :: Maybe (QualName VName) -> Exp -> TermTypeM (Size, Maybe VName)-sizeFromArg fname = dimFromExp $ SourceArg (FName fname) . bareExp---- | Any argument sizes created with 'extSize' inside the given action--- will be removed once the action finishes. This is to ensure that--- just because e.g. @n+1@ appears as a size in one branch of a--- conditional, that doesn't mean it's also available in the other--- branch.-noSizeEscape :: TermTypeM a -> TermTypeM a-noSizeEscape m = do- dimtable <- gets stateDimTable- x <- m- modify $ \s -> s {stateDimTable = dimtable}- pure x----- Control flow--tapOccurrences :: TermTypeM a -> TermTypeM (a, Occurrences)-tapOccurrences m = do- (x, occs) <- collectOccurrences m- occur occs- pure (x, occs)--collectOccurrences :: TermTypeM a -> TermTypeM (a, Occurrences)-collectOccurrences m = do- old <- gets stateOccs- modify $ \s -> s {stateOccs = mempty}- x <- m- new <- gets stateOccs- modify $ \s -> s {stateOccs = old}- pure (x, new)--alternative :: TermTypeM a -> TermTypeM b -> TermTypeM (a, b)-alternative m1 m2 = do- (x, occurs1) <- collectOccurrences $ noSizeEscape m1- (y, occurs2) <- collectOccurrences $ noSizeEscape m2- checkOccurrences occurs1- checkOccurrences occurs2- occur $ occurs1 `altOccurrences` occurs2- pure (x, y)--sequentially :: TermTypeM a -> (a -> Occurrences -> TermTypeM b) -> TermTypeM b-sequentially m1 m2 = do- (a, m1flow) <- collectOccurrences m1- (b, m2flow) <- collectOccurrences $ m2 a m1flow- occur $ m1flow `seqOccurrences` m2flow- pure b----- Consumption--occur :: Occurrences -> TermTypeM ()-occur occs = modify $ \s -> s {stateOccs = stateOccs s <> occs}---- | Proclaim that we have made read-only use of the given variable.-observe :: Ident -> TermTypeM ()-observe (Ident nm (Info t) loc) =- let als = AliasBound nm `S.insert` aliases t- in occur [observation als loc]---- | Enter a context where nothing outside can be consumed (i.e. the--- body of a function definition).-noUnique :: TermTypeM a -> TermTypeM a-noUnique m = do- (x, occs) <- collectOccurrences $ localScope f m- checkOccurrences occs- occur $ fst $ split occs- pure x- where- f scope = scope {scopeVtable = M.map set $ scopeVtable scope}-- set (BoundV l tparams t) = BoundV (max l Nonlocal) tparams t- set (OverloadedF ts pts rt) = OverloadedF ts pts rt- set EqualityF = EqualityF- set (WasConsumed loc) = WasConsumed loc-- split = unzip . map (\occ -> (occ {consumed = mempty}, occ {observed = mempty}))--removeSeminullOccurrences :: TermTypeM a -> TermTypeM a-removeSeminullOccurrences m = do- (x, occs) <- collectOccurrences m- occur $ filter (not . seminullOccurrence) occs- pure x--checkIfConsumable :: SrcLoc -> Aliasing -> TermTypeM ()-checkIfConsumable loc als = do- vtable <- asks $ scopeVtable . termScope- let boundAlias (AliasBound v) = Just v- boundAlias (AliasFree _) = Nothing- consumable v = case M.lookup v vtable of- Just (BoundV Local _ t)- | Scalar Arrow {} <- t -> False- | otherwise -> True- Just (BoundV l _ _) -> l == Local- _ -> False -- Implies name from module.- case sort $ filter (not . consumable) $ mapMaybe boundAlias $ S.toList als of- v : _ -> notConsumable loc =<< describeVar loc v- [] -> pure ()---- | Proclaim that we have written to the given variable.-consume :: SrcLoc -> Aliasing -> TermTypeM ()-consume loc als = do- checkIfConsumable loc als- occur [consumption als loc]---- | Proclaim that we have written to the given variable, and mark--- accesses to it and all of its aliases as invalid inside the given--- computation.-consuming :: Ident -> TermTypeM a -> TermTypeM a-consuming (Ident name (Info t) loc) m = do- t' <- normTypeFully t- consume loc $ AliasBound name `S.insert` aliases t'- localScope consume' m- where- consume' scope =- scope {scopeVtable = M.insert name (WasConsumed loc) $ scopeVtable scope}- -- Running initialTermScope :: TermScope@@ -998,7 +648,7 @@ TermScope { scopeVtable = initialVtable, scopeTypeTable = mempty,- scopeNameMap = topLevelNameMap,+ scopeNameMap = mempty, scopeModTable = mempty } where@@ -1008,7 +658,7 @@ arrow x y = Scalar $ Arrow mempty Unnamed Observe x y addIntrinsicF (name, IntrinsicMonoFun pts t) =- Just (name, BoundV Global [] $ arrow pts' $ RetType [] $ prim t)+ Just (name, BoundV [] $ arrow pts' $ RetType [] $ prim t) where pts' = case pts of [pt] -> prim pt@@ -1018,22 +668,40 @@ addIntrinsicF (name, IntrinsicPolyFun tvs pts rt) = Just ( name,- BoundV Global tvs $ fromStruct $ foldFunType pts rt+ BoundV tvs $ foldFunType pts rt ) addIntrinsicF (name, IntrinsicEquality) = Just (name, EqualityF) addIntrinsicF _ = Nothing -runTermTypeM :: TermTypeM a -> TypeM (a, Occurrences)-runTermTypeM (TermTypeM m) = do+runTermTypeM :: (UncheckedExp -> TermTypeM Exp) -> TermTypeM a -> TypeM a+runTermTypeM checker (TermTypeM m) = do initial_scope <- (initialTermScope <>) . envToTermScope <$> askEnv+ name <- askImportName+ outer_env <- askEnv+ src <- gets TypeM.stateNameSource let initial_tenv = TermEnv { termScope = initial_scope, termChecking = Nothing,- termLevel = 0+ termLevel = 0,+ termChecker = checker,+ termImportName = name,+ termOuterEnv = outer_env }- second stateOccs- <$> runStateT- (runReaderT m initial_tenv)- (TermTypeState mempty 0 mempty mempty mempty)+ initial_state =+ TermTypeState+ { stateConstraints = mempty,+ stateCounter = 0,+ stateUsed = mempty,+ stateWarnings = mempty,+ stateNameSource = src+ }+ case runExcept (runStateT (runReaderT m initial_tenv) initial_state) of+ Left (ws, e) -> do+ warnings ws+ throwError e+ Right (a, TermTypeState {stateNameSource, stateWarnings}) -> do+ warnings stateWarnings+ modify $ \s -> s {TypeM.stateNameSource = stateNameSource}+ pure a
src/Language/Futhark/TypeChecker/Terms/Pat.hs view
@@ -6,7 +6,6 @@ bindingIdent, bindingSizes, doNotShadow,- boundAliases, ) where @@ -18,6 +17,7 @@ import Data.Map.Strict qualified as M import Data.Maybe import Data.Set qualified as S+import Data.Text qualified as T import Futhark.Util.Pretty hiding (group, space) import Language.Futhark import Language.Futhark.TypeChecker.Monad hiding (BoundV)@@ -27,131 +27,60 @@ import Prelude hiding (mod) -- | Names that may not be shadowed.-doNotShadow :: [String]+doNotShadow :: [Name] doNotShadow = ["&&", "||"] nonrigidFor :: [SizeBinder VName] -> StructType -> TermTypeM StructType nonrigidFor [] t = pure t -- Minor optimisation. nonrigidFor sizes t = evalStateT (bitraverse onDim pure t) mempty where- onDim (NamedSize (QualName _ v))+ onDim (Var (QualName _ v) typ loc) | Just size <- find ((== v) . sizeName) sizes = do prev <- gets $ lookup v case prev of Nothing -> do v' <- lift $ newID $ baseName v- lift $ constrain v' $ Size Nothing $ mkUsage' $ srclocOf size+ lift . constrain v' . Size Nothing $+ mkUsage size "ambiguous size of bound expression" modify ((v, v') :)- pure $ NamedSize $ qualName v'+ pure $ Var (qualName v') typ loc Just v' ->- pure $ NamedSize $ qualName v'+ pure $ Var (qualName v') typ loc onDim d = pure d --- | The set of in-scope variables that are being aliased.-boundAliases :: Aliasing -> S.Set VName-boundAliases = S.map aliasVar . S.filter bound- where- bound AliasBound {} = True- bound AliasFree {} = False--checkIfUsed :: Bool -> Occurrences -> Ident -> TermTypeM ()-checkIfUsed allow_consume occs v- | not allow_consume,- not $ consumable $ unInfo $ identType v,- Just occ <- find consumes occs =- typeError (srclocOf occ) mempty $- "Consuming"- <+> dquotes (prettyName $ identName v)- <+> textwrap ("which is a non-consumable parameter bound at " <> locText (locOf v) <> ".")- | not $ identName v `S.member` allOccurring occs,- not $ "_" `isPrefixOf` baseString (identName v) =- warn (srclocOf v) $- "Unused variable" <+> dquotes (prettyName $ identName v) <+> "."- | otherwise =- pure ()- where- consumes = maybe False (identName v `S.member`) . consumed-- consumable (Scalar (Record fs)) = all consumable fs- consumable (Scalar (Sum cs)) = all (all consumable) cs- consumable (Scalar (TypeVar _ u _ _)) = u == Unique- consumable (Scalar Arrow {}) = True- consumable (Scalar Prim {}) = True- consumable (Array _ u _ _) = u == Unique- -- | Bind these identifiers locally while running the provided action.--- Checks that the identifiers are used properly within the scope--- (e.g. consumption). binding ::- -- | Allow consumption, even if the type is not unique.- Bool ->- [Ident] ->+ [Ident StructType] -> TermTypeM a -> TermTypeM a-binding allow_consume idents = check . handleVars+binding idents m =+ localScope (`bindVars` idents) $ do+ -- Those identifiers that can potentially also be sizes are+ -- added as type constraints. This is necessary so that we+ -- can properly detect scope violations during unification.+ -- We do this for *all* identifiers, not just those that are+ -- integers, because they may become integers later due to+ -- inference...+ forM_ idents $ \ident ->+ constrain (identName ident) $ ParamSize $ srclocOf ident+ m <* checkIfUsed where- handleVars m =- localScope (`bindVars` idents) $ do- -- Those identifiers that can potentially also be sizes are- -- added as type constraints. This is necessary so that we- -- can properly detect scope violations during unification.- -- We do this for *all* identifiers, not just those that are- -- integers, because they may become integers later due to- -- inference...- forM_ idents $ \ident ->- constrain (identName ident) $ ParamSize $ srclocOf ident- m-- bindVars :: TermScope -> [Ident] -> TermScope bindVars = foldl bindVar - bindVar :: TermScope -> Ident -> TermScope bindVar scope (Ident name (Info tp) _) =- let inedges = boundAliases $ aliases tp- update (BoundV l tparams in_t)- | Array {} <- tp = BoundV l tparams (in_t `addAliases` S.insert (AliasBound name))- | otherwise = BoundV l tparams in_t- update b = b-- tp' = tp `addAliases` S.insert (AliasBound name)- in scope- { scopeVtable =- M.insert name (BoundV Local [] tp') $- adjustSeveral update inedges $- scopeVtable scope- }-- adjustSeveral f = flip $ foldl $ flip $ M.adjust f-- -- Check whether the bound variables have been used correctly- -- within their scope.- check m = do- (a, usages) <- collectBindingsOccurrences m- checkOccurrences usages-- mapM_ (checkIfUsed allow_consume usages) idents-- pure a+ scope+ { scopeVtable =+ M.insert name (BoundV [] tp) $ scopeVtable scope+ } - -- Collect and remove all occurences of @idents@. This relies- -- on the fact that no variables shadow any other.- collectBindingsOccurrences m = do- (x, usage) <- collectOccurrences m- let (relevant, rest) = split usage- occur rest- pure (x, relevant)- where- onOcc occ =- let (obs1, obs2) = divide $ observed occ- occ_cons = divide <$> consumed occ- con1 = fst <$> occ_cons- con2 = snd <$> occ_cons- in ( occ {observed = obs1, consumed = con1},- occ {observed = obs2, consumed = con2}- )- split = unzip . map onOcc- names = S.fromList $ map identName idents- divide s = (s `S.intersection` names, s `S.difference` names)+ checkIfUsed = do+ used <- gets stateUsed+ forM_ (filter ((`S.notMember` used) . identName) idents) $ \ident ->+ unless ("_" `T.isPrefixOf` nameToText (baseName (identName ident))) $+ warn ident $+ "Unused variable "+ <> dquotes (prettyName (identName ident))+ <> "." bindingTypes :: [Either (VName, TypeBinding) (VName, Constraint)] ->@@ -170,32 +99,32 @@ bindingTypeParams :: [TypeParam] -> TermTypeM a -> TermTypeM a bindingTypeParams tparams =- binding False (mapMaybe typeParamIdent tparams)+ binding (mapMaybe typeParamIdent tparams) . bindingTypes (concatMap typeParamType tparams) where typeParamType (TypeParamType l v loc) =- [ Left (v, TypeAbbr l [] $ RetType [] $ Scalar (TypeVar () Nonunique (qualName v) [])),+ [ Left (v, TypeAbbr l [] $ RetType [] $ Scalar (TypeVar mempty (qualName v) [])), Right (v, ParamType l loc) ] typeParamType (TypeParamDim v loc) = [Right (v, ParamSize loc)] -typeParamIdent :: TypeParam -> Maybe Ident+typeParamIdent :: TypeParam -> Maybe (Ident StructType) typeParamIdent (TypeParamDim v loc) = Just $ Ident v (Info $ Scalar $ Prim $ Signed Int64) loc typeParamIdent _ = Nothing -- | Bind a single term-level identifier. bindingIdent ::- IdentBase NoInfo Name ->- PatType ->- (Ident -> TermTypeM a) ->+ IdentBase NoInfo Name StructType ->+ StructType ->+ (Ident StructType -> TermTypeM a) -> TermTypeM a bindingIdent (Ident v NoInfo vloc) t m = bindSpaced [(Term, v)] $ do v' <- checkName Term v vloc let ident = Ident v' (Info t) vloc- binding True [ident] $ m ident+ binding [ident] $ m ident -- | Bind @let@-bound sizes. This is usually followed by 'bindingPat' -- immediately afterwards.@@ -205,7 +134,7 @@ foldM_ lookForDuplicates mempty sizes bindSpaced (map sizeWithSpace sizes) $ do sizes' <- mapM check sizes- binding False (map sizeWithType sizes') $ m sizes'+ binding (map sizeWithType sizes') $ m sizes' where lookForDuplicates prev size | Just prevloc <- M.lookup (sizeName size) prev =@@ -227,54 +156,32 @@ sizeBinderToParam :: SizeBinder VName -> UncheckedTypeParam sizeBinderToParam (SizeBinder v loc) = TypeParamDim (baseName v) loc --- | Check and bind a @let@-pattern.-bindingPat ::- [SizeBinder VName] ->- PatBase NoInfo Name ->- InferredType ->- (Pat -> TermTypeM a) ->- TermTypeM a-bindingPat sizes p t m = do- checkPat sizes p t $ \p' -> binding True (S.toList $ patIdents p') $ do- -- Perform an observation of every declared dimension. This- -- prevents unused-name warnings for otherwise unused dimensions.- let ident (SizeBinder v loc) =- Ident v (Info (Scalar $ Prim $ Signed Int64)) loc- mapM_ (observe . ident) sizes-- let used_sizes = freeInType $ patternStructType p'- case filter ((`S.notMember` used_sizes) . sizeName) sizes of- [] -> m p'- size : _ -> unusedSize size- -- All this complexity is just so we can handle un-suffixed numeric -- literals in patterns.-patLitMkType :: PatLit -> SrcLoc -> TermTypeM StructType+patLitMkType :: PatLit -> SrcLoc -> TermTypeM ParamType patLitMkType (PatLitInt _) loc = do t <- newTypeVar loc "t"- mustBeOneOf anyNumberType (mkUsage loc "integer literal") t+ mustBeOneOf anyNumberType (mkUsage loc "integer literal") (toStruct t) pure t patLitMkType (PatLitFloat _) loc = do t <- newTypeVar loc "t"- mustBeOneOf anyFloatType (mkUsage loc "float literal") t+ mustBeOneOf anyFloatType (mkUsage loc "float literal") (toStruct t) pure t patLitMkType (PatLitPrim v) _ = pure $ Scalar $ Prim $ primValueType v checkPat' :: [SizeBinder VName] ->- UncheckedPat ->- InferredType ->- TermTypeM Pat+ UncheckedPat ParamType ->+ Inferred ParamType ->+ TermTypeM (Pat ParamType) checkPat' sizes (PatParens p loc) t = PatParens <$> checkPat' sizes p t <*> pure loc checkPat' sizes (PatAttr attr p loc) t = PatAttr <$> checkAttr attr <*> checkPat' sizes p t <*> pure loc checkPat' _ (Id name _ loc) _- | name' `elem` doNotShadow =- typeError loc mempty $ "The" <+> pretty name' <+> "operator may not be redefined."- where- name' = nameToString name+ | name `elem` doNotShadow =+ typeError loc mempty $ "The" <+> pretty name <+> "operator may not be redefined." checkPat' _ (Id name NoInfo loc) (Ascribed t) = do name' <- newID name pure $ Id name' (Info t) loc@@ -295,7 +202,7 @@ <*> pure loc checkPat' sizes p@(TuplePat ps loc) (Ascribed t) = do ps_t <- replicateM (length ps) (newTypeVar loc "t")- unify (mkUsage loc "matching a tuple pattern") (Scalar (tupleRecord ps_t)) $ toStruct t+ unify (mkUsage loc "matching a tuple pattern") (Scalar (tupleRecord ps_t)) (toStruct t) t' <- normTypeFully t checkPat' sizes p $ Ascribed t' checkPat' sizes (TuplePat ps loc) NoneInferred =@@ -319,7 +226,7 @@ typeError loc mempty $ "Duplicate fields in record pattern" <+> pretty p <> "." - unify (mkUsage loc "matching a record pattern") (Scalar (Record fields')) $ toStruct t+ unify (mkUsage loc "matching a record pattern") (Scalar (Record fields')) (toStruct t) t' <- normTypeFully t checkPat' sizes p $ Ascribed t' checkPat' sizes (RecordPat fs loc) NoneInferred =@@ -331,26 +238,25 @@ case maybe_outer_t of Ascribed outer_t -> do- st_forunify <- nonrigidFor sizes st+ st_forunify <- nonrigidFor sizes $ toStruct st unify (mkUsage loc "explicit type ascription") st_forunify (toStruct outer_t) - outer_t' <- normTypeFully outer_t PatAscription- <$> checkPat' sizes p (Ascribed (addAliasesFromType (fromStruct st) outer_t'))+ <$> checkPat' sizes p (Ascribed (resToParam st)) <*> pure t' <*> pure loc NoneInferred -> PatAscription- <$> checkPat' sizes p (Ascribed (fromStruct st))+ <$> checkPat' sizes p (Ascribed (resToParam st)) <*> pure t' <*> pure loc checkPat' _ (PatLit l NoInfo loc) (Ascribed t) = do t' <- patLitMkType l loc- unify (mkUsage loc "matching against literal") t' (toStruct t)- pure $ PatLit l (Info (fromStruct t')) loc+ unify (mkUsage loc "matching against literal") (toStruct t') (toStruct t)+ pure $ PatLit l (Info t') loc checkPat' _ (PatLit l NoInfo loc) NoneInferred = do t' <- patLitMkType l loc- pure $ PatLit l (Info (fromStruct t')) loc+ pure $ PatLit l (Info t') loc checkPat' sizes (PatConstr n NoInfo ps loc) (Ascribed (Scalar (Sum cs))) | Just ts <- M.lookup n cs = do when (length ps /= length ts) $@@ -366,8 +272,8 @@ t' <- newTypeVar loc "t" ps' <- forM ps $ \p -> do p_t <- newTypeVar (srclocOf p) "t"- checkPat' sizes p $ Ascribed $ addAliasesFromType p_t t- mustHaveConstr usage n t' (patternStructType <$> ps')+ checkPat' sizes p $ Ascribed p_t+ mustHaveConstr usage n (toStruct t') (patternStructType <$> ps') unify usage t' (toStruct t) t'' <- normTypeFully t pure $ PatConstr n (Info t'') ps' loc@@ -376,25 +282,22 @@ checkPat' sizes (PatConstr n NoInfo ps loc) NoneInferred = do ps' <- mapM (\p -> checkPat' sizes p NoneInferred) ps t <- newTypeVar loc "t"- mustHaveConstr usage n t (patternStructType <$> ps')- pure $ PatConstr n (Info $ fromStruct t) ps' loc+ mustHaveConstr usage n (toStruct t) (patternStructType <$> ps')+ pure $ PatConstr n (Info t) ps' loc where usage = mkUsage loc "matching against constructor" -patNameMap :: Pat -> NameMap-patNameMap = M.fromList . map asTerm . S.toList . patNames- where- asTerm v = ((Term, baseName v), qualName v)- checkPat :: [SizeBinder VName] ->- UncheckedPat ->- InferredType ->- (Pat -> TermTypeM a) ->+ UncheckedPat (TypeBase Size u) ->+ Inferred StructType ->+ (Pat ParamType -> TermTypeM a) -> TermTypeM a checkPat sizes p t m = do checkForDuplicateNames (map sizeBinderToParam sizes) [p]- p' <- onFailure (CheckingPat p t) $ checkPat' sizes p t+ p' <-+ onFailure (CheckingPat (fmap toStruct p) t) $+ checkPat' sizes (fmap (toParam Observe) p) (fmap (toParam Observe) t) let explicit = mustBeExplicitInType $ patternStructType p' @@ -407,23 +310,36 @@ [] -> bindNameMap (patNameMap p') $ m p' +-- | Check and bind a @let@-pattern.+bindingPat ::+ [SizeBinder VName] ->+ UncheckedPat (TypeBase Size u) ->+ StructType ->+ (Pat ParamType -> TermTypeM a) ->+ TermTypeM a+bindingPat sizes p t m = do+ checkPat sizes p (Ascribed t) $ \p' -> binding (patIdents (fmap toStruct p')) $+ case filter ((`S.notMember` fvVars (freeInPat p')) . sizeName) sizes of+ [] -> m p'+ size : _ -> unusedSize size++patNameMap :: Pat t -> NameMap+patNameMap = M.fromList . map asTerm . patNames+ where+ asTerm v = ((Term, baseName v), qualName v)+ -- | Check and bind type and value parameters. bindingParams :: [UncheckedTypeParam] ->- [UncheckedPat] ->- ([TypeParam] -> [Pat] -> TermTypeM a) ->+ [UncheckedPat ParamType] ->+ ([TypeParam] -> [Pat ParamType] -> TermTypeM a) -> TermTypeM a bindingParams tps orig_ps m = do checkForDuplicateNames tps orig_ps checkTypeParams tps $ \tps' -> bindingTypeParams tps' $ do let descend ps' (p : ps) = checkPat [] p NoneInferred $ \p' ->- binding False (S.toList $ patIdents p') $ descend (p' : ps') ps- descend ps' [] = do- -- Perform an observation of every type parameter. This- -- prevents unused-name warnings for otherwise unused- -- dimensions.- mapM_ observe $ mapMaybe typeParamIdent tps'- m tps' $ reverse ps'+ binding (patIdents $ fmap toStruct p') $ descend (p' : ps') ps+ descend ps' [] = m tps' $ reverse ps' descend [] orig_ps
src/Language/Futhark/TypeChecker/Types.hs view
@@ -2,10 +2,6 @@ module Language.Futhark.TypeChecker.Types ( checkTypeExp, renameRetType,- subtypeOf,- subuniqueOf,- returnType,- addAliasesFromType, checkForDuplicateNames, checkTypeParams, typeParamToArg,@@ -29,7 +25,6 @@ import Data.Bifunctor import Data.List (find, foldl', sort, unzip4, (\\)) import Data.Map.Strict qualified as M-import Data.Maybe import Data.Set qualified as S import Futhark.Util (nubOrd) import Futhark.Util.Pretty@@ -41,15 +36,13 @@ mustBeExplicitAux t = execState (traverseDims onDim t) mempty where- onDim bound _ (NamedSize d)+ onDim bound _ (Var d _ _) | qualLeaf d `S.member` bound = modify $ \s -> M.insertWith (&&) (qualLeaf d) False s- onDim _ PosImmediate (NamedSize d) =+ onDim _ PosImmediate (Var d _ _) = modify $ \s -> M.insertWith (&&) (qualLeaf d) False s- onDim _ _ (NamedSize d) =- modify $ M.insertWith (&&) (qualLeaf d) True- onDim _ _ _ =- pure ()+ onDim _ _ e =+ modify $ flip (S.foldr (\v -> M.insertWith (&&) v True)) $ fvVars $ freeInExp e -- | Determine which of the sizes in a type are used as sizes outside -- of functions in the type, and which are not. The former are said@@ -68,12 +61,8 @@ mustBeExplicitInBinding :: StructType -> S.Set VName mustBeExplicitInBinding bind_t = let (ts, ret) = unfoldFunType bind_t- alsoRet =- M.unionWith (&&) $- M.fromList $- zip (S.toList $ freeInType ret) $- repeat True- in S.fromList $ M.keys $ M.filter id $ alsoRet $ foldl' onType mempty $ map snd ts+ alsoRet = M.unionWith (&&) $ M.fromList $ zip (S.toList (fvVars (freeInType ret))) (repeat True)+ in S.fromList $ M.keys $ M.filter id $ alsoRet $ foldl' onType mempty $ map toStruct ts where onType uses t = uses <> mustBeExplicitAux t -- Left-biased union. @@ -83,188 +72,26 @@ mustBeExplicitInType :: StructType -> S.Set VName mustBeExplicitInType = snd . determineSizeWitnesses --- | @returnType appres ret_type arg_diet arg_type@ gives result of applying--- an argument the given types to a function with the given return--- type, consuming the argument with the given diet.-returnType :: Aliasing -> PatType -> Diet -> PatType -> PatType-returnType _ (Array _ Unique et shape) _ _ =- Array mempty Nonunique et shape -- Intentional!-returnType appres (Array als Nonunique et shape) d arg =- Array (appres <> als <> arg_als) Nonunique et shape- where- arg_als = aliases $ maskAliases arg d-returnType appres (Scalar (Record fs)) d arg =- Scalar $ Record $ fmap (\et -> returnType appres et d arg) fs-returnType _ (Scalar (Prim t)) _ _ =- Scalar $ Prim t-returnType _ (Scalar (TypeVar _ Unique t targs)) _ _ =- Scalar $ TypeVar mempty Nonunique t targs -- Intentional!-returnType appres (Scalar (TypeVar als Nonunique t targs)) d arg =- Scalar $ TypeVar (appres <> als <> arg_als) Unique t targs- where- arg_als = aliases $ maskAliases arg d-returnType _ (Scalar (Arrow old_als v pd t1 (RetType dims t2))) d arg =- Scalar $ Arrow als v pd (t1 `setAliases` mempty) $ RetType dims $ t2 `setAliases` als- where- -- Make sure to propagate the aliases of an existing closure.- als = old_als <> aliases (maskAliases arg d)-returnType appres (Scalar (Sum cs)) d arg =- Scalar $ Sum $ (fmap . fmap) (\et -> returnType appres et d arg) cs---- @t `maskAliases` d@ removes aliases (sets them to 'mempty') from--- the parts of @t@ that are denoted as consumed by the 'Diet' @d@.-maskAliases ::- Monoid as =>- TypeBase shape as ->- Diet ->- TypeBase shape as-maskAliases t Consume = t `setAliases` mempty-maskAliases t Observe = t---- | The two types are assumed to be structurally equal, but not--- necessarily regarding sizes. Combines aliases.-addAliasesFromType :: PatType -> PatType -> PatType-addAliasesFromType (Array als1 u1 et1 shape1) (Array als2 _ _ _) =- Array (als1 <> als2) u1 et1 shape1-addAliasesFromType- (Scalar (TypeVar als1 u1 tv1 targs1))- (Scalar (TypeVar als2 _ _ _)) =- Scalar $ TypeVar (als1 <> als2) u1 tv1 targs1-addAliasesFromType (Scalar (Record ts1)) (Scalar (Record ts2))- | length ts1 == length ts2,- sort (M.keys ts1) == sort (M.keys ts2) =- Scalar $ Record $ M.intersectionWith addAliasesFromType ts1 ts2-addAliasesFromType- (Scalar (Arrow als1 mn1 d1 pt1 (RetType dims1 rt1)))- (Scalar (Arrow als2 _ _ _ (RetType _ rt2))) =- Scalar (Arrow (als1 <> als2) mn1 d1 pt1 (RetType dims1 rt1'))- where- rt1' = addAliasesFromType rt1 rt2-addAliasesFromType (Scalar (Sum cs1)) (Scalar (Sum cs2))- | length cs1 == length cs2,- sort (M.keys cs1) == sort (M.keys cs2) =- Scalar $ Sum $ M.intersectionWith (zipWith addAliasesFromType) cs1 cs2-addAliasesFromType (Scalar (Prim t)) _ = Scalar $ Prim t-addAliasesFromType t1 t2 =- error $ "addAliasesFromType invalid args: " ++ show (t1, t2)---- | @unifyTypes uf t1 t2@ attempts to unify @t1@ and @t2@. If--- unification cannot happen, 'Nothing' is returned, otherwise a type--- that combines the aliasing of @t1@ and @t2@ is returned.--- Uniqueness is unified with @uf@. Assumes sizes already match, and--- always picks the size of the leftmost type.-unifyTypesU ::- (Monoid als) =>- (Uniqueness -> Uniqueness -> Maybe Uniqueness) ->- TypeBase dim als ->- TypeBase dim als ->- Maybe (TypeBase dim als)-unifyTypesU uf (Array als1 u1 shape1 et1) (Array als2 u2 _shape2 et2) =- Array (als1 <> als2)- <$> uf u1 u2- <*> pure shape1- <*> unifyScalarTypes uf et1 et2-unifyTypesU uf (Scalar t1) (Scalar t2) = Scalar <$> unifyScalarTypes uf t1 t2-unifyTypesU _ _ _ = Nothing--unifyScalarTypes ::- (Monoid als) =>- (Uniqueness -> Uniqueness -> Maybe Uniqueness) ->- ScalarTypeBase dim als ->- ScalarTypeBase dim als ->- Maybe (ScalarTypeBase dim als)-unifyScalarTypes _ (Prim t1) (Prim t2)- | t1 == t2 = Just $ Prim t1- | otherwise = Nothing-unifyScalarTypes uf (TypeVar als1 u1 tv1 targs1) (TypeVar als2 u2 tv2 targs2)- | tv1 == tv2 = do- u3 <- uf u1 u2- targs3 <- zipWithM unifyTypeArgs targs1 targs2- Just $ TypeVar (als1 <> als2) u3 tv1 targs3- | otherwise = Nothing- where- unifyTypeArgs (TypeArgDim d1 loc) (TypeArgDim _d2 _) =- pure $ TypeArgDim d1 loc- unifyTypeArgs (TypeArgType t1 loc) (TypeArgType t2 _) =- TypeArgType <$> unifyTypesU uf t1 t2 <*> pure loc- unifyTypeArgs _ _ =- Nothing-unifyScalarTypes uf (Record ts1) (Record ts2)- | length ts1 == length ts2,- sort (M.keys ts1) == sort (M.keys ts2) =- Record- <$> traverse- (uncurry (unifyTypesU uf))- (M.intersectionWith (,) ts1 ts2)-unifyScalarTypes- uf- (Arrow as1 mn1 d1 t1 (RetType dims1 t1'))- (Arrow as2 _ _ t2 (RetType _ t2')) =- Arrow (as1 <> as2) mn1 d1- <$> unifyTypesU (flip uf) t1 t2- <*> (RetType dims1 <$> unifyTypesU uf t1' t2')-unifyScalarTypes uf (Sum cs1) (Sum cs2)- | length cs1 == length cs2,- sort (M.keys cs1) == sort (M.keys cs2) =- Sum- <$> traverse- (uncurry (zipWithM (unifyTypesU uf)))- (M.intersectionWith (,) cs1 cs2)-unifyScalarTypes _ _ _ = Nothing---- | @x \`subtypeOf\` y@ is true if @x@ is a subtype of @y@ (or equal--- to @y@), meaning @x@ is valid whenever @y@ is. Ignores sizes.--- Mostly used for checking uniqueness.-subtypeOf :: TypeBase () () -> TypeBase () () -> Bool-subtypeOf t1 t2 = isJust $ unifyTypesU unifyUniqueness (toStruct t1) (toStruct t2)- where- unifyUniqueness u2 u1 = if u2 `subuniqueOf` u1 then Just u1 else Nothing---- | @x `subuniqueOf` y@ is true if @x@ is not less unique than @y@.-subuniqueOf :: Uniqueness -> Uniqueness -> Bool-subuniqueOf Nonunique Unique = False-subuniqueOf _ _ = True- -- | Ensure that the dimensions of the RetType are unique by -- generating new names for them. This is to avoid name capture.-renameRetType :: MonadTypeChecker m => StructRetType -> m StructRetType+renameRetType :: MonadTypeChecker m => ResRetType -> m ResRetType renameRetType (RetType dims st) | dims /= mempty = do dims' <- mapM newName dims- let m = M.fromList $ zip dims $ map (SizeSubst . NamedSize . qualName) dims'+ let mkSubst = ExpSubst . flip sizeFromName mempty . qualName+ m = M.fromList . zip dims $ map mkSubst dims' st' = applySubst (`M.lookup` m) st pure $ RetType dims' st' | otherwise = pure $ RetType dims st -checkExpForSize ::- MonadTypeChecker m =>- ExpBase NoInfo Name ->- m (Exp, Size)-checkExpForSize (IntLit x NoInfo loc) =- pure (IntLit x int64_info loc, ConstSize $ fromInteger x)- where- int64_info = Info (Scalar (Prim (Signed Int64)))-checkExpForSize (Literal (SignedValue (Int64Value x)) loc) =- pure (Literal (SignedValue (Int64Value x)) loc, ConstSize x)-checkExpForSize (Var v NoInfo vloc) = do- v' <- checkNamedSize vloc v- pure (Var v' int64_info vloc, NamedSize v')- where- int64_info = Info (Scalar (Prim (Signed Int64)))-checkExpForSize e =- typeError- (locOf e)- mempty- "Only variables and i64 literals are allowed in size expressions."- evalTypeExp :: MonadTypeChecker m => TypeExp NoInfo Name ->- m (TypeExp Info VName, [VName], StructRetType, Liftedness)+ m (TypeExp Info VName, [VName], ResRetType, Liftedness) evalTypeExp (TEVar name loc) = do (name', ps, t, l) <- lookupType loc name- t' <- renameRetType t+ t' <- renameRetType $ toResRet Nonunique t case ps of [] -> pure (TEVar name' loc, [], t', l) _ ->@@ -308,7 +135,7 @@ evalTypeExp (TEArray d t loc) = do (d_svars, d', d'') <- checkSizeExp d (t', svars, RetType dims st, l) <- evalTypeExp t- case (l, arrayOf Nonunique (Shape [d'']) st) of+ case (l, arrayOfWithAliases Nonunique (Shape [d'']) st) of (Unlifted, st') -> pure ( TEArray d' t' loc,@@ -329,10 +156,10 @@ where checkSizeExp (SizeExpAny dloc) = do dv <- newTypeName "d"- pure ([dv], SizeExpAny dloc, NamedSize $ qualName dv)+ pure ([dv], SizeExpAny dloc, sizeFromName (qualName dv) dloc) checkSizeExp (SizeExp e dloc) = do- (e', sz) <- checkExpForSize e- pure ([], SizeExp e' dloc, sz)+ e' <- checkExpForSize e+ pure ([], SizeExp e' dloc, e') -- evalTypeExp (TEUnique t loc) = do (t', svars, RetType dims st, l) <- evalTypeExp t@@ -352,12 +179,12 @@ (t1', svars1, RetType dims1 st1, _) <- evalTypeExp t1 bindSpaced [(Term, v)] $ do v' <- checkName Term v loc- bindVal v' (BoundV [] st1) $ do+ bindVal v' (BoundV [] $ toStruct st1) $ do (t2', svars2, RetType dims2 st2, _) <- evalTypeExp t2 pure ( TEArrow (Just v') t1' t2' loc, svars1 ++ dims1 ++ svars2,- RetType [] $ Scalar $ Arrow mempty (Named v') (diet st1) st1 (RetType dims2 st2),+ RetType [] $ Scalar $ Arrow Nonunique (Named v') (diet $ resToParam st1) (toStruct st1) (RetType dims2 st2), Lifted ) --@@ -368,7 +195,7 @@ ( TEArrow Nothing t1' t2' loc, svars1 ++ dims1 ++ svars2, RetType [] . Scalar $- Arrow mempty Unnamed (diet st1) (st1 `setUniqueness` Nonunique) $+ Arrow Nonunique Unnamed (diet $ resToParam st1) (toStruct st1) $ RetType dims2 st2, Lifted )@@ -378,7 +205,7 @@ dims' <- mapM (flip (checkName Term) loc) dims bindDims dims' $ do (t', svars, RetType t_dims st, l) <- evalTypeExp t- let (witnessed, _) = determineSizeWitnesses st+ let (witnessed, _) = determineSizeWitnesses $ toStruct st case find (`S.notMember` witnessed) dims' of Just d -> typeError loc mempty . withIndexLink "unused-existential" $@@ -423,7 +250,7 @@ evalTypeExp ote@TEApply {} = do (tname, tname_loc, targs) <- rootAndArgs ote (tname', ps, tname_t, l) <- lookupType tloc tname- RetType t_dims t <- renameRetType tname_t+ RetType t_dims t <- renameRetType $ toResRet Nonunique tname_t if length ps /= length targs then typeError tloc mempty $@@ -438,7 +265,8 @@ pure ( foldl (\x y -> TEApply x y tloc) (TEVar tname' tname_loc) targs', [],- RetType (t_dims ++ mconcat dims) $ applySubst (`M.lookup` mconcat substs) t,+ RetType (t_dims ++ mconcat dims) $+ applySubst (`M.lookup` mconcat substs) t, l ) where@@ -457,18 +285,18 @@ "Type" <+> dquotes (pretty te') <+> "is not a type constructor." checkSizeExp (SizeExp e dloc) = do- (e', sz) <- checkExpForSize e+ e' <- checkExpForSize e pure ( TypeArgExpSize (SizeExp e' dloc), [],- SizeSubst sz+ ExpSubst e' ) checkSizeExp (SizeExpAny loc) = do d <- newTypeName "d" pure ( TypeArgExpSize (SizeExpAny loc), [d],- SizeSubst $ NamedSize $ qualName d+ ExpSubst $ sizeFromName (qualName d) loc ) checkArgApply (TypeParamDim pv _) (TypeArgExpSize d) = do@@ -479,7 +307,7 @@ pure ( TypeArgExpType te', svars ++ dims,- M.singleton pv $ Subst [] $ RetType [] st+ M.singleton pv $ Subst [] $ RetType [] $ toStruct st ) checkArgApply p a = typeError tloc mempty $@@ -497,14 +325,14 @@ checkTypeExp :: MonadTypeChecker m => TypeExp NoInfo Name ->- m (TypeExp Info VName, [VName], StructRetType, Liftedness)+ m (TypeExp Info VName, [VName], ResRetType, Liftedness) checkTypeExp te = do checkForDuplicateNamesInType te evalTypeExp te -- | Check for duplication of names inside a binding group. checkForDuplicateNames ::- MonadTypeChecker m => [UncheckedTypeParam] -> [UncheckedPat] -> m ()+ MonadTypeChecker m => [UncheckedTypeParam] -> [UncheckedPat t] -> m () checkForDuplicateNames tps pats = (`evalStateT` mempty) $ do mapM_ checkTypeParam tps mapM_ checkPat pats@@ -618,23 +446,25 @@ -- | Construct a type argument corresponding to a type parameter. typeParamToArg :: TypeParam -> StructTypeArg typeParamToArg (TypeParamDim v ploc) =- TypeArgDim (NamedSize $ qualName v) ploc-typeParamToArg (TypeParamType _ v ploc) =- TypeArgType (Scalar $ TypeVar () Nonunique (qualName v) []) ploc+ TypeArgDim $ sizeFromName (qualName v) ploc+typeParamToArg (TypeParamType _ v _) =+ TypeArgType $ Scalar $ TypeVar mempty (qualName v) [] -- | A type substitution may be a substitution or a yet-unknown -- substitution (but which is certainly an overloaded primitive--- type!). The latter is used to remove aliases from types that are--- yet-unknown but that we know cannot carry aliases (see issue #682).-data Subst t = Subst [TypeParam] t | PrimSubst | SizeSubst Size+-- type!).+data Subst t = Subst [TypeParam] t | ExpSubst Exp deriving (Show) instance Pretty t => Pretty (Subst t) where pretty (Subst [] t) = pretty t pretty (Subst tps t) = mconcat (map pretty tps) <> colon <+> pretty t- pretty PrimSubst = "#<primsubst>"- pretty (SizeSubst d) = pretty d+ pretty (ExpSubst e) = pretty e +instance Functor Subst where+ fmap f (Subst ps t) = Subst ps $ f t+ fmap _ (ExpSubst e) = ExpSubst e+ -- | Create a type substitution corresponding to a type binding. substFromAbbr :: TypeBinding -> Subst StructRetType substFromAbbr (TypeAbbr _ ps rt) = Subst ps rt@@ -642,55 +472,75 @@ -- | Substitutions to apply in a type. type TypeSubs = VName -> Maybe (Subst StructRetType) -instance Functor Subst where- fmap f (Subst ps t) = Subst ps $ f t- fmap _ PrimSubst = PrimSubst- fmap _ (SizeSubst v) = SizeSubst v- -- | Class of types which allow for substitution of types with no -- annotations for type variable names. class Substitutable a where applySubst :: TypeSubs -> a -> a -instance Substitutable (RetTypeBase Size ()) where- applySubst f (RetType dims t) =- let RetType more_dims t' = substTypesRet f t- in RetType (dims ++ more_dims) t'--instance Substitutable (RetTypeBase Size Aliasing) where+instance Substitutable (RetTypeBase Size Uniqueness) where applySubst f (RetType dims t) = let RetType more_dims t' = substTypesRet f' t in RetType (dims ++ more_dims) t' where f' = fmap (fmap (second (const mempty))) . f -instance Substitutable (TypeBase Size ()) where+instance Substitutable (RetTypeBase Size NoUniqueness) where+ applySubst f (RetType dims t) =+ let RetType more_dims t' = substTypesRet f t+ in RetType (dims ++ more_dims) t'++instance Substitutable StructType where applySubst = substTypesAny -instance Substitutable (TypeBase Size Aliasing) where- applySubst = substTypesAny . (fmap (fmap (second (const mempty))) .)+instance Substitutable ParamType where+ applySubst f = substTypesAny $ fmap (fmap $ second $ const Observe) . f -instance Substitutable Size where- applySubst f (NamedSize (QualName _ v))- | Just (SizeSubst d) <- f v = d- applySubst _ d = d+instance Substitutable (TypeBase Size Uniqueness) where+ applySubst f = substTypesAny $ fmap (fmap $ second $ const Nonunique) . f +instance Substitutable Exp where+ applySubst f = runIdentity . mapOnExp+ where+ mapOnExp (Var (QualName _ v) _ _)+ | Just (ExpSubst e') <- f v = pure e'+ mapOnExp e' = astMap mapper e'++ mapper =+ ASTMapper+ { mapOnExp,+ mapOnName = pure,+ mapOnStructType = pure . applySubst f,+ mapOnParamType = pure . applySubst f,+ mapOnResRetType = pure . applySubst f+ }+ instance Substitutable d => Substitutable (Shape d) where applySubst f = fmap $ applySubst f -instance Substitutable Pat where+instance Substitutable (Pat StructType) where applySubst f = runIdentity . astMap mapper where mapper = ASTMapper- { mapOnExp = pure,+ { mapOnExp = pure . applySubst f, mapOnName = pure, mapOnStructType = pure . applySubst f,- mapOnPatType = pure . applySubst f,- mapOnStructRetType = pure . applySubst f,- mapOnPatRetType = pure . applySubst f+ mapOnParamType = pure . applySubst f,+ mapOnResRetType = pure . applySubst f } +instance Substitutable (Pat ParamType) where+ applySubst f = runIdentity . astMap mapper+ where+ mapper =+ ASTMapper+ { mapOnExp = pure . applySubst f,+ mapOnName = pure,+ mapOnStructType = pure . applySubst f,+ mapOnParamType = pure . applySubst f,+ mapOnResRetType = pure . applySubst f+ }+ applyType :: Monoid als => [TypeParam] ->@@ -701,9 +551,9 @@ where substs = M.fromList $ zipWith mkSubst ps args -- We are assuming everything has already been type-checked for correctness.- mkSubst (TypeParamDim pv _) (TypeArgDim d _) =- (pv, SizeSubst d)- mkSubst (TypeParamType _ pv _) (TypeArgType at _) =+ mkSubst (TypeParamDim pv _) (TypeArgDim e) =+ (pv, ExpSubst e)+ mkSubst (TypeParamType _ pv _) (TypeArgType at) = (pv, Subst [] $ RetType [] $ second mempty at) mkSubst p a = error $ "applyType mkSubst: cannot substitute " ++ prettyString a ++ " for " ++ prettyString p@@ -733,7 +583,8 @@ else do let start = maximum $ map baseTag seen_ext ext' = zipWith VName (map baseName ext) [start + 1 ..]- extsubsts = M.fromList $ zip ext $ map (SizeSubst . NamedSize . qualName) ext'+ mkSubst = ExpSubst . flip sizeFromName mempty . qualName+ extsubsts = M.fromList $ zip ext $ map mkSubst ext' RetType [] t' = substTypesRet (`M.lookup` extsubsts) t pure $ RetType ext' t' @@ -743,24 +594,19 @@ TypeBase Size as -> State [VName] (TypeBase Size as) - onType (Array als u shape et) = do- t <- arrayOf u (applySubst lookupSubst' shape) <$> onType (Scalar et)- pure $ t `setAliases` als+ onType (Array u shape et) =+ arrayOfWithAliases u (applySubst lookupSubst' shape)+ <$> onType (second (const mempty) $ Scalar et) onType (Scalar (Prim t)) = pure $ Scalar $ Prim t- onType (Scalar (TypeVar als u v targs)) = do+ onType (Scalar (TypeVar u v targs)) = do targs' <- mapM subsTypeArg targs case lookupSubst $ qualLeaf v of Just (Subst ps rt) -> do RetType ext t <- freshDims rt modify (ext ++)- pure $- applyType ps (t `setAliases` mempty) targs'- `setUniqueness` u- `addAliases` (<> als)- Just PrimSubst ->- pure $ Scalar $ TypeVar mempty u v targs'+ pure $ second (<> u) $ applyType ps (second (const u) t) targs' _ ->- pure $ Scalar $ TypeVar als u v targs'+ pure $ Scalar $ TypeVar u v targs' onType (Scalar (Record ts)) = Scalar . Record <$> traverse onType ts onType (Scalar (Arrow als v d t1 t2)) =@@ -779,14 +625,14 @@ _ -> pure $ RetType (new_ext <> dims) t' - subsTypeArg (TypeArgType t loc) = do+ subsTypeArg (TypeArgType t) = do let RetType dims t' = substTypesRet lookupSubst' t modify (dims ++)- pure $ TypeArgType t' loc- subsTypeArg (TypeArgDim v loc) =- pure $ TypeArgDim (applySubst lookupSubst' v) loc+ pure $ TypeArgType t'+ subsTypeArg (TypeArgDim v) =+ pure $ TypeArgDim $ applySubst lookupSubst' v - lookupSubst' = fmap (fmap $ second (const ())) . lookupSubst+ lookupSubst' = fmap (fmap $ second (const NoUniqueness)) . lookupSubst -- | Perform substitutions, from type names to types, on a type. Works -- regardless of what shape and uniqueness information is attached to the type.@@ -803,8 +649,7 @@ -- AnySize. This should _never_ happen during type-checking, but -- may happen as we substitute types during monomorphisation and -- defunctorisation later on. See Note [AnySize]- let toAny (NamedSize v)- | qualLeaf v `elem` dims = AnySize Nothing+ let toAny (Var v _ _) | qualLeaf v `elem` dims = anySize toAny d = d in first toAny ot'
src/Language/Futhark/TypeChecker/Unify.hs view
@@ -20,11 +20,9 @@ mustHaveField, mustBeOneOf, equalityType,- normPatType,+ normType, normTypeFully,- instantiateEmptyArrayDims, unify,- expect, unifyMostCommon, doUnification, )@@ -33,8 +31,6 @@ import Control.Monad import Control.Monad.Except import Control.Monad.State-import Data.Bifunctor-import Data.Char (isAscii) import Data.List (foldl', intersect) import Data.Map.Strict qualified as M import Data.Maybe@@ -97,13 +93,13 @@ deriving (Show) -- | Construct a 'Usage' from a location and a description.-mkUsage :: SrcLoc -> T.Text -> Usage-mkUsage = flip (Usage . Just)+mkUsage :: Located a => a -> T.Text -> Usage+mkUsage = flip (Usage . Just) . srclocOf -- | Construct a 'Usage' that has just a location, but no particular -- description.-mkUsage' :: SrcLoc -> Usage-mkUsage' = Usage Nothing+mkUsage' :: Located a => a -> Usage+mkUsage' = Usage Nothing . srclocOf instance Pretty Usage where pretty (Usage Nothing loc) = "use at " <> textwrap (locText loc)@@ -129,12 +125,12 @@ | ParamSize SrcLoc | -- | Is not actually a type, but a term-level size, -- possibly already set to something specific.- Size (Maybe Size) Usage+ Size (Maybe Exp) Usage | -- | A size that does not unify with anything - -- created from the result of applying a function -- whose return size is existential, or otherwise -- hiding a size.- UnknowableSize SrcLoc RigidSource+ UnknownSize SrcLoc RigidSource deriving (Show) instance Located Constraint where@@ -147,7 +143,7 @@ locOf (HasConstrs _ _ usage) = locOf usage locOf (ParamSize loc) = locOf loc locOf (Size _ usage) = locOf usage- locOf (UnknowableSize loc _) = locOf loc+ locOf (UnknownSize loc _) = locOf loc -- | Mapping from fresh type variables, instantiated from the type -- schemes of polymorphic functions, to (possibly) specific types as@@ -158,9 +154,8 @@ lookupSubst :: VName -> Constraints -> Maybe (Subst StructRetType) lookupSubst v constraints = case snd <$> M.lookup v constraints of Just (Constraint t _) -> Just $ Subst [] $ applySubst (`lookupSubst` constraints) t- Just Overloaded {} -> Just PrimSubst Just (Size (Just d) _) ->- Just $ SizeSubst $ applySubst (`lookupSubst` constraints) d+ Just $ ExpSubst $ applySubst (`lookupSubst` constraints) d _ -> Nothing -- | The source of a rigid size.@@ -251,11 +246,11 @@ -- | Retrieve notes describing the purpose or origin of the given -- t'Size'. The location is used as the *current* location, for the -- purpose of reporting relative locations.-dimNotes :: (Located a, MonadUnify m) => a -> Size -> m Notes-dimNotes ctx (NamedSize d) = do+dimNotes :: (Located a, MonadUnify m) => a -> Exp -> m Notes+dimNotes ctx (Var d _ _) = do c <- M.lookup (qualLeaf d) <$> getConstraints case c of- Just (_, UnknowableSize loc rsrc) ->+ Just (_, UnknownSize loc rsrc) -> pure . aNote $ dquotes (pretty d) <+> prettySource (srclocOf ctx) loc rsrc _ -> pure mempty@@ -264,8 +259,9 @@ typeNotes :: (Located a, MonadUnify m) => a -> StructType -> m Notes typeNotes ctx = fmap mconcat- . mapM (dimNotes ctx . NamedSize . qualName)+ . mapM (dimNotes ctx . flip sizeFromName mempty . qualName) . S.toList+ . fvVars . freeInType typeVarNotes :: MonadUnify m => VName -> m Notes@@ -300,7 +296,11 @@ putConstraints $ f x newTypeVar :: Monoid als => SrcLoc -> Name -> m (TypeBase dim als)- newDimVar :: SrcLoc -> Rigidity -> Name -> m VName+ newDimVar :: Usage -> Rigidity -> Name -> m VName+ newRigidDim :: Located a => a -> RigidSource -> Name -> m VName+ newRigidDim loc = newDimVar (mkUsage' loc) . Rigid+ newFlexibleDim :: Usage -> Name -> m VName+ newFlexibleDim usage = newDimVar usage Nonrigid curLevel :: m Level @@ -329,27 +329,17 @@ -- | Replace any top-level type variable with its substitution. normType :: MonadUnify m => StructType -> m StructType-normType t@(Scalar (TypeVar _ _ (QualName [] v) [])) = do+normType t@(Scalar (TypeVar _ (QualName [] v) [])) = do constraints <- getConstraints case snd <$> M.lookup v constraints of Just (Constraint (RetType [] t') _) -> normType t' _ -> pure t normType t = pure t --- | Replace any top-level type variable with its substitution.-normPatType :: MonadUnify m => PatType -> m PatType-normPatType t@(Scalar (TypeVar als u (QualName [] v) [])) = do- constraints <- getConstraints- case snd <$> M.lookup v constraints of- Just (Constraint (RetType [] t') _) ->- normPatType $ t' `setUniqueness` u `setAliases` als- _ -> pure t-normPatType t = pure t- rigidConstraint :: Constraint -> Bool rigidConstraint ParamType {} = True rigidConstraint ParamSize {} = True-rigidConstraint UnknowableSize {} = True+rigidConstraint UnknownSize {} = True rigidConstraint _ = False unsharedConstructorsMsg :: M.Map Name t -> M.Map Name t -> Doc a@@ -360,22 +350,6 @@ filter (`notElem` M.keys cs1) (M.keys cs2) ++ filter (`notElem` M.keys cs2) (M.keys cs1) --- | Instantiate existential context in return type.-instantiateEmptyArrayDims ::- MonadUnify m =>- SrcLoc ->- Rigidity ->- RetTypeBase Size als ->- m (TypeBase Size als, [VName])-instantiateEmptyArrayDims tloc r (RetType dims t) = do- dims' <- mapM new dims- pure (first (onDim $ zip dims dims') t, dims')- where- new = newDimVar tloc r . nameFromString . takeWhile isAscii . baseString- onDim dims' (NamedSize d) =- NamedSize $ maybe d qualName (lookup (qualLeaf d) dims')- onDim _ d = d- -- | Is the given type variable the name of an abstract type or type -- parameter, which we cannot substitute? isRigid :: VName -> Constraints -> Bool@@ -389,16 +363,16 @@ guard $ not $ rigidConstraint c pure lvl -type UnifyDims m =- BreadCrumbs -> [VName] -> (VName -> Maybe Int) -> Size -> Size -> m ()+type UnifySizes m =+ BreadCrumbs -> [VName] -> (VName -> Maybe Int) -> Exp -> Exp -> m () -flipUnifyDims :: UnifyDims m -> UnifyDims m-flipUnifyDims onDims bcs bound nonrigid t1 t2 =+flipUnifySizes :: UnifySizes m -> UnifySizes m+flipUnifySizes onDims bcs bound nonrigid t1 t2 = onDims bcs bound nonrigid t2 t1 unifyWith :: MonadUnify m =>- UnifyDims m ->+ UnifySizes m -> Usage -> [VName] -> BreadCrumbs ->@@ -426,12 +400,12 @@ -- We may have to flip the order of future calls to -- onDims inside linkVarToType. linkDims- | ord' = flipUnifyDims onDims+ | ord' = flipUnifySizes onDims | otherwise = onDims - unifyTypeArg bcs' (TypeArgDim d1 _) (TypeArgDim d2 _) =+ unifyTypeArg bcs' (TypeArgDim d1) (TypeArgDim d2) = onDims' bcs' (swap ord d1 d2)- unifyTypeArg bcs' (TypeArgType t _) (TypeArgType arg_t _) =+ unifyTypeArg bcs' (TypeArgType t) (TypeArgType arg_t) = subunify ord bound bcs' t arg_t unifyTypeArg bcs' _ _ = unifyError@@ -449,6 +423,8 @@ (applySubst (`lookupSubst` constraints) d2) case (t1', t2') of+ (Scalar (Prim pt1), Scalar (Prim pt2))+ | pt1 == pt2 -> pure () ( Scalar (Record fs), Scalar (Record arg_fs) )@@ -460,15 +436,15 @@ ++ filter (`notElem` M.keys fs) (M.keys arg_fs) unifyError usage mempty bcs $ "Unshared fields:" <+> commasep (map pretty missing) <> "."- ( Scalar (TypeVar _ _ (QualName _ tn) targs),- Scalar (TypeVar _ _ (QualName _ arg_tn) arg_targs)+ ( Scalar (TypeVar _ (QualName _ tn) targs),+ Scalar (TypeVar _ (QualName _ arg_tn) arg_targs) ) | tn == arg_tn, length targs == length arg_targs -> do let bcs' = breadCrumb (Matching "When matching type arguments.") bcs zipWithM_ (unifyTypeArg bcs') targs arg_targs- ( Scalar (TypeVar _ _ (QualName [] v1) []),- Scalar (TypeVar _ _ (QualName [] v2) [])+ ( Scalar (TypeVar _ (QualName [] v1) []),+ Scalar (TypeVar _ (QualName [] v2) []) ) -> case (nonrigid v1, nonrigid v2) of (Nothing, Nothing) -> failure@@ -477,10 +453,10 @@ (Just lvl1, Just lvl2) | lvl1 <= lvl2 -> link ord v1 lvl1 t2' | otherwise -> link (not ord) v2 lvl2 t1'- (Scalar (TypeVar _ _ (QualName [] v1) []), _)+ (Scalar (TypeVar _ (QualName [] v1) []), _) | Just lvl <- nonrigid v1 -> link ord v1 lvl t2'- (_, Scalar (TypeVar _ _ (QualName [] v2) []))+ (_, Scalar (TypeVar _ (QualName [] v2) [])) | Just lvl <- nonrigid v2 -> link (not ord) v2 lvl t1' ( Scalar (Arrow _ p1 d1 a1 (RetType b1_dims b1)),@@ -493,6 +469,13 @@ </> "and" </> indent 2 (pretty d2 <> pretty a2) </> "are incompatible regarding consuming their arguments."+ | uncurry (<) $ swap ord (uniqueness b2) (uniqueness b1) -> do+ unifyError usage mempty bcs . withIndexLink "unify-return-uniqueness" $+ "Return types"+ </> indent 2 (pretty d1 <> pretty b1)+ </> "and"+ </> indent 2 (pretty d2 <> pretty b2)+ </> "have incompatible uniqueness." | otherwise -> do -- Introduce the existentials as size variables so they -- are subject to unification. We will remove them again@@ -501,7 +484,7 @@ swap ord (Size Nothing $ Usage Nothing mempty)- (UnknowableSize mempty RigidUnify)+ (UnknownSize mempty RigidUnify) lvl <- curLevel modifyConstraints (M.fromList (zip b1_dims $ repeat (lvl, r1)) <>) modifyConstraints (M.fromList (zip b2_dims $ repeat (lvl, r2)) <>)@@ -517,8 +500,8 @@ ord bound' (breadCrumb (Matching "When matching return types.") bcs)- b1'- b2'+ (toStruct b1')+ (toStruct b2') -- Delete the size variables we introduced to represent -- the existential sizes.@@ -532,7 +515,7 @@ case (p1, p2) of (Named p1', Named p2') -> let f v- | v == p2' = Just $ SizeSubst $ NamedSize $ qualName p1'+ | v == p2' = Just $ ExpSubst $ sizeFromName (qualName p1') mempty | otherwise = Nothing in (b1, applySubst f b2) (_, _) ->@@ -554,59 +537,35 @@ unifySharedConstructors onDims usage bound bcs cs arg_cs | otherwise -> unifyError usage mempty bcs $ unsharedConstructorsMsg arg_cs cs- _- | t1' == t2' -> pure ()- | otherwise -> failure+ _ -> failure -unifyDims :: MonadUnify m => Usage -> UnifyDims m-unifyDims _ _ _ _ d1 d2- | d1 == d2 = pure ()-unifyDims usage bcs _ nonrigid (NamedSize (QualName _ d1)) d2- | Just lvl1 <- nonrigid d1 =- linkVarToDim usage bcs d1 lvl1 d2-unifyDims usage bcs _ nonrigid d1 (NamedSize (QualName _ d2))- | Just lvl2 <- nonrigid d2 =- linkVarToDim usage bcs d2 lvl2 d1-unifyDims usage bcs _ _ d1 d2 = do- notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2+anyBound :: [VName] -> ExpBase Info VName -> Bool+anyBound bound e = any (`S.member` fvVars (freeInExp e)) bound++unifySizes :: MonadUnify m => Usage -> UnifySizes m+unifySizes usage bcs bound nonrigid e1 e2+ | Just es <- similarExps e1 e2 =+ mapM_ (uncurry $ unifySizes usage bcs bound nonrigid) es+unifySizes usage bcs bound nonrigid (Var v1 _ _) e2+ | Just lvl1 <- nonrigid (qualLeaf v1),+ not (anyBound bound e2) || (qualLeaf v1 `elem` bound) =+ linkVarToDim usage bcs (qualLeaf v1) lvl1 e2+unifySizes usage bcs bound nonrigid e1 (Var v2 _ _)+ | Just lvl2 <- nonrigid (qualLeaf v2),+ not (anyBound bound e1) || (qualLeaf v2 `elem` bound) =+ linkVarToDim usage bcs (qualLeaf v2) lvl2 e1+unifySizes usage bcs _ _ e1 e2 = do+ notes <- (<>) <$> dimNotes usage e2 <*> dimNotes usage e2 unifyError usage notes bcs $- "Dimensions"- <+> dquotes (pretty d1)+ "Sizes"+ <+> dquotes (pretty e1) <+> "and"- <+> dquotes (pretty d2)+ <+> dquotes (pretty e2) <+> "do not match." -- | Unifies two types. unify :: MonadUnify m => Usage -> StructType -> StructType -> m ()-unify usage = unifyWith (unifyDims usage) usage mempty noBreadCrumbs---- | @expect super sub@ checks that @sub@ is a subtype of @super@.-expect :: MonadUnify m => Usage -> StructType -> StructType -> m ()-expect usage = unifyWith onDims usage mempty noBreadCrumbs- where- onDims _ _ _ d1 d2- | d1 == d2 = pure ()- -- We identify existentially bound names by them being nonrigid- -- and yet bound. It's OK to unify with those.- onDims bcs bound nonrigid (NamedSize (QualName _ d1)) d2- | Just lvl1 <- nonrigid d1,- not (boundParam bound d2) || (d1 `elem` bound) =- linkVarToDim usage bcs d1 lvl1 d2- onDims bcs bound nonrigid d1 (NamedSize (QualName _ d2))- | Just lvl2 <- nonrigid d2,- not (boundParam bound d1) || (d2 `elem` bound) =- linkVarToDim usage bcs d2 lvl2 d1- onDims bcs _ _ d1 d2 = do- notes <- (<>) <$> dimNotes usage d1 <*> dimNotes usage d2- unifyError usage notes bcs $- "Dimensions"- <+> dquotes (pretty d1)- <+> "and"- <+> dquotes (pretty d2)- <+> "do not match."-- boundParam bound (NamedSize (QualName _ d)) = d `elem` bound- boundParam _ _ = False+unify usage = unifyWith (unifySizes usage) usage mempty noBreadCrumbs occursCheck :: MonadUnify m =>@@ -636,7 +595,7 @@ checkType constraints tp where checkType constraints t =- mapM_ (check constraints) $ typeVars t <> freeInType t+ mapM_ (check constraints) $ typeVars t <> fvVars (freeInType t) check constraints v | Just (lvl, c) <- M.lookup v constraints,@@ -661,7 +620,7 @@ linkVarToType :: MonadUnify m =>- UnifyDims m ->+ UnifySizes m -> Usage -> [VName] -> BreadCrumbs ->@@ -706,7 +665,7 @@ link arrayElemTypeWith usage (unliftedBcs unlift_usage) tp- when (any (`elem` bound) (freeInType tp)) $+ when (any (`elem` bound) (fvVars (freeInType tp))) $ unifyError usage mempty bcs $ "Type variable" <+> prettyName vn@@ -720,7 +679,7 @@ | tp `notElem` map (Scalar . Prim) ts -> do link case tp of- Scalar (TypeVar _ _ (QualName [] v) [])+ Scalar (TypeVar _ (QualName [] v) []) | not $ isRigid v constraints -> linkVarToTypes usage v ts _ ->@@ -742,11 +701,11 @@ | all (`M.member` tp_fields) $ M.keys required_fields -> do required_fields' <- mapM normTypeFully required_fields let tp' = Scalar $ Record $ required_fields <> tp_fields -- Crucially left-biased.- ext = filter (`S.member` freeInType tp') bound+ ext = filter (`S.member` fvVars (freeInType tp')) bound modifyConstraints $ M.insert vn (lvl, Constraint (RetType ext tp') usage) unifySharedFields onDims usage bound bcs required_fields' tp_fields- Scalar (TypeVar _ _ (QualName [] v) []) -> do+ Scalar (TypeVar _ (QualName [] v) []) -> do case M.lookup v constraints of Just (_, HasFields _ tp_fields _) -> unifySharedFields onDims usage bound bcs required_fields tp_fields@@ -784,13 +743,13 @@ Scalar (Sum ts) | all (`M.member` ts) $ M.keys required_cs -> do let tp' = Scalar $ Sum $ required_cs <> ts -- Crucially left-biased.- ext = filter (`S.member` freeInType tp') bound+ ext = filter (`S.member` fvVars (freeInType tp')) bound modifyConstraints $ M.insert vn (lvl, Constraint (RetType ext tp') usage) unifySharedConstructors onDims usage bound bcs required_cs ts | otherwise -> unsharedConstructors required_cs ts =<< typeVarNotes vn- Scalar (TypeVar _ _ (QualName [] v) []) -> do+ Scalar (TypeVar _ (QualName [] v) []) -> do case M.lookup v constraints of Just (_, HasConstrs _ v_cs _) -> unifySharedConstructors onDims usage bound bcs required_cs v_cs@@ -837,31 +796,41 @@ BreadCrumbs -> VName -> Level ->- Size ->+ Exp -> m ()-linkVarToDim usage bcs vn lvl dim = do+linkVarToDim usage bcs vn lvl e = do constraints <- getConstraints - case dim of- NamedSize dim'- | Just (dim_lvl, c) <- qualLeaf dim' `M.lookup` constraints,- dim_lvl > lvl ->+ mapM_ (checkVar constraints) $ fvVars $ freeInExp e++ modifyConstraints $ M.insert vn (lvl, Size (Just e) usage)+ where+ checkVar constraints dim'+ | Just (dim_lvl, c) <- dim' `M.lookup` constraints,+ dim_lvl > lvl = case c of ParamSize {} -> do- notes <- dimNotes usage dim+ notes <- dimNotes usage e unifyError usage notes bcs $ "Cannot unify size variable"- <+> dquotes (pretty dim')+ <+> dquotes (pretty e) <+> "with" <+> dquotes (prettyName vn) <+> "(scope violation)." </> "This is because"- <+> dquotes (pretty dim')+ <+> dquotes (pretty $ qualName dim') <+> "is rigidly bound in a deeper scope."- _ -> modifyConstraints $ M.insert (qualLeaf dim') (lvl, c)- _ -> pure ()-- modifyConstraints $ M.insert vn (lvl, Size (Just dim) usage)+ _ -> modifyConstraints $ M.insert dim' (lvl, c)+ checkVar _ dim'+ | vn == dim' = do+ notes <- dimNotes usage e+ unifyError usage notes bcs $+ "Occurs check: cannot instantiate"+ <+> dquotes (prettyName vn)+ <+> "with"+ <+> dquotes (pretty e)+ <+> "."+ checkVar _ _ = pure () -- | Assert that this type must be one of the given primitive types. mustBeOneOf :: MonadUnify m => [PrimType] -> Usage -> StructType -> m ()@@ -872,7 +841,7 @@ let isRigid' v = isRigid v constraints case t' of- Scalar (TypeVar _ _ (QualName [] v) [])+ Scalar (TypeVar _ (QualName [] v) []) | not $ isRigid' v -> linkVarToTypes usage v ts Scalar (Prim pt) | pt `elem` ts -> pure () _ -> failure@@ -919,9 +888,9 @@ -- | Assert that this type must support equality. equalityType ::- (MonadUnify m, Pretty (Shape dim), Monoid as) =>+ (MonadUnify m, Pretty (Shape dim), Pretty u) => Usage ->- TypeBase dim as ->+ TypeBase dim u -> m () equalityType usage t = do unless (orderZero t) $@@ -932,7 +901,7 @@ mustBeEquality vn = do constraints <- getConstraints case M.lookup vn constraints of- Just (_, Constraint (RetType [] (Scalar (TypeVar _ _ (QualName [] vn') []))) _) ->+ Just (_, Constraint (RetType [] (Scalar (TypeVar _ (QualName [] vn') []))) _) -> mustBeEquality vn' Just (_, Constraint (RetType _ vn_t) cusage) | not $ orderZero vn_t ->@@ -995,10 +964,10 @@ bc = Matching $ "When checking" <+> textwrap desc arrayElemTypeWith ::- (MonadUnify m, Pretty (Shape dim), Monoid as) =>+ (MonadUnify m, Pretty (Shape dim), Pretty u) => Usage -> BreadCrumbs ->- TypeBase dim as ->+ TypeBase dim u -> m () arrayElemTypeWith usage bcs t = do unless (orderZero t) $@@ -1023,10 +992,10 @@ -- | Assert that this type must be valid as an array element. arrayElemType ::- (MonadUnify m, Pretty (Shape dim), Monoid as) =>+ (MonadUnify m, Pretty (Shape dim), Pretty u) => Usage -> T.Text ->- TypeBase dim as ->+ TypeBase dim u -> m () arrayElemType usage desc = arrayElemTypeWith usage $ breadCrumb bc noBreadCrumbs@@ -1035,7 +1004,7 @@ unifySharedFields :: MonadUnify m =>- UnifyDims m ->+ UnifySizes m -> Usage -> [VName] -> BreadCrumbs ->@@ -1048,7 +1017,7 @@ unifySharedConstructors :: MonadUnify m =>- UnifyDims m ->+ UnifySizes m -> Usage -> [VName] -> BreadCrumbs ->@@ -1079,7 +1048,7 @@ mustHaveConstr usage c t fs = do constraints <- getConstraints case t of- Scalar (TypeVar _ _ (QualName _ tn) [])+ Scalar (TypeVar _ (QualName _ tn) []) | Just (lvl, NoConstraint l _) <- M.lookup tn constraints -> do mapM_ (scopeCheck usage noBreadCrumbs tn lvl) fs modifyConstraints $ M.insert tn (lvl, HasConstrs l (M.singleton c fs) usage)@@ -1108,23 +1077,22 @@ mustHaveFieldWith :: MonadUnify m =>- UnifyDims m ->+ UnifySizes m -> Usage -> [VName] -> BreadCrumbs -> Name ->- PatType ->- m PatType+ StructType ->+ m StructType mustHaveFieldWith onDims usage bound bcs l t = do constraints <- getConstraints l_type <- newTypeVar (srclocOf usage) "t"- let l_type' = l_type `setAliases` aliases t case t of- Scalar (TypeVar _ _ (QualName _ tn) [])+ Scalar (TypeVar _ (QualName _ tn) []) | Just (lvl, NoConstraint {}) <- M.lookup tn constraints -> do scopeCheck usage bcs tn lvl l_type modifyConstraints $ M.insert tn (lvl, HasFields Lifted (M.singleton l l_type) usage)- pure l_type'+ pure l_type | Just (lvl, HasFields lifted fields _) <- M.lookup tn constraints -> do case M.lookup l fields of Just t' -> unifyWith onDims usage bound bcs l_type t'@@ -1133,10 +1101,10 @@ M.insert tn (lvl, HasFields lifted (M.insert l l_type fields) usage)- pure l_type'+ pure l_type Scalar (Record fields) | Just t' <- M.lookup l fields -> do- unify usage l_type $ toStruct t'+ unify usage l_type t' pure t' | otherwise -> unifyError usage mempty bcs $@@ -1145,29 +1113,29 @@ <+> " of value of type" <+> pretty (toStructural t) <> "." _ -> do- unify usage (toStruct t) $ Scalar $ Record $ M.singleton l l_type- pure l_type'+ unify usage t $ Scalar $ Record $ M.singleton l l_type+ pure l_type -- | Assert that some type must have a field with this name and type. mustHaveField :: MonadUnify m => Usage -> Name ->- PatType ->- m PatType-mustHaveField usage = mustHaveFieldWith (unifyDims usage) usage mempty noBreadCrumbs+ StructType ->+ m StructType+mustHaveField usage = mustHaveFieldWith (unifySizes usage) usage mempty noBreadCrumbs newDimOnMismatch ::- (Monoid as, MonadUnify m) =>+ MonadUnify m => SrcLoc ->- TypeBase Size as ->- TypeBase Size as ->- m (TypeBase Size as, [VName])+ StructType ->+ StructType ->+ m (StructType, [VName]) newDimOnMismatch loc t1 t2 = do (t, seen) <- runStateT (matchDims onDims t1 t2) mempty pure (t, M.elems seen) where- r = Rigid $ RigidCond (toStruct t1) (toStruct t2)+ r = RigidCond t1 t2 onDims _ d1 d2 | d1 == d2 = pure d1 | otherwise = do@@ -1175,25 +1143,25 @@ -- same new size. maybe_d <- gets $ M.lookup (d1, d2) case maybe_d of- Just d -> pure $ NamedSize $ qualName d+ Just d -> pure $ sizeFromName (qualName d) loc Nothing -> do- d <- lift $ newDimVar loc r "differ"+ d <- lift $ newRigidDim loc r "differ" modify $ M.insert (d1, d2) d- pure $ NamedSize $ qualName d+ pure $ sizeFromName (qualName d) loc -- | Like unification, but creates new size variables where mismatches -- occur. Returns the new dimensions thus created. unifyMostCommon :: MonadUnify m => Usage ->- PatType ->- PatType ->- m (PatType, [VName])+ StructType ->+ StructType ->+ m (StructType, [VName]) unifyMostCommon usage t1 t2 = do -- We are ignoring the dimensions here, because any mismatches -- should be turned into fresh size variables. let allOK _ _ _ _ _ = pure ()- unifyWith allOK usage mempty noBreadCrumbs (toStruct t1) (toStruct t2)+ unifyWith allOK usage mempty noBreadCrumbs t1 t2 t1' <- normTypeFully t1 t2' <- normTypeFully t2 newDimOnMismatch (srclocOf usage) t1' t2'@@ -1224,13 +1192,17 @@ newTypeVar loc name = do v <- newVar name modifyConstraints $ M.insert v (0, NoConstraint Lifted $ Usage Nothing loc)- pure $ Scalar $ TypeVar mempty Nonunique (qualName v) []+ pure $ Scalar $ TypeVar mempty (qualName v) [] - newDimVar loc rigidity name = do+ newDimVar usage rigidity name = do dim <- newVar name case rigidity of- Rigid src -> modifyConstraints $ M.insert dim (0, UnknowableSize loc src)- Nonrigid -> modifyConstraints $ M.insert dim (0, Size Nothing $ Usage Nothing loc)+ Rigid src ->+ modifyConstraints $+ M.insert dim (0, UnknownSize (srclocOf usage) src)+ Nonrigid ->+ modifyConstraints $+ M.insert dim (0, Size Nothing usage) pure dim curLevel = pure 0@@ -1274,7 +1246,7 @@ Either TypeError StructType doUnification loc rigid_tparams nonrigid_tparams t1 t2 = runUnifyM rigid_tparams nonrigid_tparams $ do- expect (Usage Nothing (srclocOf loc)) t1 t2+ unify (Usage Nothing (srclocOf loc)) t1 t2 normTypeFully t2 -- Note [Linking variables to sum types]
unittests/Futhark/IR/SyntaxTests.hs view
@@ -2,5 +2,22 @@ module Futhark.IR.SyntaxTests () where --- There isn't anything to test in this module. At some point, maybe--- we can put some Arbitrary instances here.+import Data.String+import Data.Text qualified as T+import Futhark.IR.Parse+import Futhark.IR.Syntax++-- There isn't anything to test in this module, but we define some+-- convenience instances.++instance IsString DeclExtType where+ fromString =+ either (error . T.unpack) id+ . parseDeclExtType "IsString DeclExtType"+ . T.pack++instance IsString DeclType where+ fromString =+ either (error . T.unpack) id+ . parseDeclType "IsString DeclType"+ . T.pack
+ unittests/Futhark/Internalise/TypesValuesTests.hs view
@@ -0,0 +1,173 @@+module Futhark.Internalise.TypesValuesTests (tests) where++import Control.Monad.Free (Free (..))+import Data.Map qualified as M+import Data.String (fromString)+import Futhark.IR.Syntax hiding (Free)+import Futhark.IR.SyntaxTests ()+import Futhark.Internalise.TypesValues+import Language.Futhark.SyntaxTests ()+import Test.Tasty+import Test.Tasty.HUnit++internaliseTypeTests :: TestTree+internaliseTypeTests =+ testGroup+ "internaliseType"+ [ mkTest+ "[0]()"+ [Free [Pure "[0i64]unit"]],+ mkTest+ "{a: [t_7447][n_7448](f32, f32), b: i64, c: i64}"+ [Free [Pure "[t_7447][n_7448]f32", Pure "[t_7447][n_7448]f32"], Pure "i64", Pure "i64"],+ mkTest+ "([0]i32, {a: f32, b: f32, c: f32, d: [0]((f32, f32), (f32, f32))})"+ [ Free [Pure "[0i64]i32"],+ Pure "f32",+ Pure "f32",+ Pure "f32",+ Free [Pure "[0i64]f32", Pure "[0i64]f32", Pure "[0i64]f32", Pure "[0i64]f32"]+ ],+ mkTest+ "[0]([1]i32, f32)"+ [Free [Free [Pure "[0i64][1i64]i32"], Pure "[0i64]f32"]]+ ]+ where+ mkTest x y =+ testCase (prettyString x) $ internaliseType x @?= y++sumTypeTests :: TestTree+sumTypeTests =+ testGroup+ "internaliseConstructors"+ [ testCase "Dedup of primitives" $+ internaliseConstructors+ ( M.fromList+ [ ("foo", [Pure "i64"]),+ ("bar", [Pure "i64"])+ ]+ )+ @?= ( [Pure "i64"],+ M.fromList+ [ ("foo", (1, [0])),+ ("bar", (0, [0]))+ ]+ ),+ testCase "Dedup of array" $+ internaliseConstructors+ ( M.fromList+ [ ("foo", [Pure "[?0]i64"]),+ ("bar", [Pure "[?0]i64"])+ ]+ )+ @?= ( [Pure "[?0]i64"],+ M.fromList+ [ ("foo", (1, [0])),+ ("bar", (0, [0]))+ ]+ ),+ testCase+ "Dedup of array of tuple"+ $ internaliseConstructors+ ( M.fromList+ [ ("foo", [Free [Pure "[?0]i64", Pure "[?0]i64"]]),+ ("bar", [Pure "[?0]i64"])+ ]+ )+ @?= ( [Pure "[?0]i64", Free [Pure "[?0]i64", Pure "[?0]i64"]],+ M.fromList+ [ ("foo", (1, [1, 2])),+ ("bar", (0, [0]))+ ]+ )+ ]++-- Be aware that some of these tests simply reinforce current+-- behaviour - it may be that we want to restrict aliasing even+-- further in the future; these tests would have to be updated in such+-- cases.+inferAliasesTests :: TestTree+inferAliasesTests =+ testGroup+ "inferAliases"+ [ mkTest+ [Free [Pure "[0i64]i32"]]+ [Free [Pure "[?0]i32"]]+ [[("[?0]i32", RetAls [0] [0])]],+ mkTest+ [Free [Pure "[0i64]i32", Pure "[0i64]i32"]]+ [Free [Pure "[0i64]i32", Pure "[0i64]i32"]]+ [ [ ("[0i64]i32", RetAls [0] [0]),+ ("[0i64]i32", RetAls [1] [1])+ ]+ ],+ -- Basically zip.+ mkTest+ [Free [Pure "[n_0]i32"], Free [Pure "[n_0]i32"]]+ [Free [Pure "[n_0]i32", Pure "[n_0]i32"]]+ [ [ ("[n_0]i32", RetAls [] [0]),+ ("[n_0]i32", RetAls [] [1])+ ]+ ],+ mkTest+ [Free [Pure "[0i64]i32"], Free [Pure "[0i64]i32", Pure "[0i64]i32"]]+ [Free [Pure "[?0]i32", Pure "[?0]i32"]]+ [ [ ("[?0]i32", RetAls [1] [0]),+ ("[?0]i32", RetAls [2] [1])+ ]+ ],+ mkTest+ [Free [Pure "[0i64][1i64]i32", Pure "[0i64][1i64]i32"]]+ [Free [Pure "[?0]i32", Pure "[?0]i32"]]+ [ [ ("[?0]i32", RetAls [0] [0]),+ ("[?0]i32", RetAls [1] [1])+ ]+ ],+ -- Basically unzip.+ mkTest+ [Free [Pure "[n_0][n_1]i32", Pure "[n_0][n_1]i32"]]+ [Free [Pure "[?0]i32"], Free [Pure "[?0]i32"]]+ [ [("[?0]i32", RetAls [] [0, 1])],+ [("[?0]i32", RetAls [] [0, 1])]+ ],+ mkTest+ [ Free [Pure "*[n_0][n_1]i32"],+ Free [Pure "[n_2]i64"],+ Free [Pure "[n_3]i64"]+ ]+ [Free [Pure "*[n_0][n_1]i32"]]+ [[("*[n_0][n_1]i32", RetAls [] [])]],+ mkTest+ [Free [Pure "[n_0]i32", Free [Pure "[n_0][n_1]i32"]]]+ [Free [Pure "[n_0]i32"]]+ [[("[n_0]i32", RetAls [1] [0])]],+ mkTest+ []+ [ Free [Pure "[n_0]i32", Free [Pure "[n_0][n_1]i32"]],+ Free [Pure "[n_0]i32"]+ ]+ [ [("[n_0]i32", RetAls [] [0]), ("[n_0][n_1]i32", RetAls [] [1])],+ [("[n_0]i32", RetAls [] [1, 2])]+ ],+ mkTest+ [Free [Pure "[n_0]i32"]]+ [Free [Pure "[m_1][m_1]i32"]]+ [ [("[m_1][m_1]i32", RetAls [0] [0])]+ ]+ ]+ where+ mkTest all_param_ts all_res_ts expected =+ testCase (show all_param_ts <> " " <> show all_res_ts) $+ inferAliases+ (map (fmap fromString) all_param_ts)+ (map (fmap fromString) all_res_ts)+ @?= expected++tests :: TestTree+tests =+ testGroup+ "Futhark.Internalise.TypesValuesTests"+ [ internaliseTypeTests,+ sumTypeTests,+ inferAliasesTests+ ]
unittests/Language/Futhark/SyntaxTests.hs view
@@ -3,6 +3,7 @@ module Language.Futhark.SyntaxTests (tests) where import Control.Applicative hiding (many, some)+import Data.Bifunctor import Data.Char (isAlpha) import Data.Functor import Data.Map qualified as M@@ -115,67 +116,74 @@ pSize = brackets $ choice- [ ConstSize <$> lexeme L.decimal,- NamedSize <$> pQualName+ [ flip sizeFromInteger mempty <$> lexeme L.decimal,+ flip sizeFromName mempty <$> pQualName ] -pScalarNonFun :: Parser (ScalarTypeBase Size ())+pScalarNonFun :: Parser (ScalarTypeBase Size Uniqueness) pScalarNonFun = choice [ Prim <$> pPrimType, pTypeVar,- tupleRecord <$> parens (pStructType `sepBy1` lexeme ","),+ tupleRecord <$> parens (pType `sepBy` lexeme ","), Record . M.fromList <$> braces (pField `sepBy1` lexeme ",") ] where- pField = (,) <$> pName <* lexeme ":" <*> pStructType- pTypeVar = TypeVar () <$> pUniqueness <*> pQualName <*> many pTypeArg+ pField = (,) <$> pName <* lexeme ":" <*> pType+ pTypeVar = TypeVar <$> pUniqueness <*> pQualName <*> many pTypeArg pTypeArg = choice- [ TypeArgDim <$> pSize <*> pure mempty,- TypeArgType <$> pTypeArgType <*> pure mempty+ [ TypeArgDim <$> pSize,+ TypeArgType . second (const NoUniqueness) <$> pTypeArgType ] pTypeArgType = choice [ Scalar . Prim <$> pPrimType,- parens pStructType+ parens pType ] -pArrayType :: Parser StructType+pArrayType :: Parser ResType pArrayType =- Array () <$> pUniqueness <*> (Shape <$> some pSize) <*> pScalarNonFun+ Array+ <$> pUniqueness+ <*> (Shape <$> some pSize)+ <*> (second (const NoUniqueness) <$> pScalarNonFun) -pNonFunType :: Parser StructType+pNonFunType :: Parser ResType pNonFunType =- choice [try pArrayType, try $ parens pStructType, Scalar <$> pScalarNonFun]+ choice+ [ try pArrayType,+ try $ parens pType,+ Scalar <$> pScalarNonFun+ ] -pScalarType :: Parser (ScalarTypeBase Size ())+pScalarType :: Parser (ScalarTypeBase Size Uniqueness) pScalarType = choice [try pFun, pScalarNonFun] where pFun =- pParam <* lexeme "->" <*> pStructRetType+ pParam <* lexeme "->" <*> pRetType pParam = choice [ try pNamedParam, do t <- pNonFunType- pure $ Arrow () Unnamed (diet t) t+ pure $ Arrow Nonunique Unnamed (diet $ resToParam t) (toStruct t) ] pNamedParam = parens $ do v <- pVName <* lexeme ":"- t <- pStructType- pure $ Arrow () (Named v) (diet t) t+ t <- pType+ pure $ Arrow Nonunique (Named v) (diet $ resToParam t) (toStruct t) -pStructRetType :: Parser StructRetType-pStructRetType =+pRetType :: Parser ResRetType+pRetType = choice- [ lexeme "?" *> (RetType <$> some (brackets pVName) <* lexeme "." <*> pStructType),- RetType [] <$> pStructType+ [ lexeme "?" *> (RetType <$> some (brackets pVName) <* lexeme "." <*> pType),+ RetType [] <$> pType ] -pStructType :: Parser StructType-pStructType =- choice [try $ Scalar <$> pScalarType, pArrayType, parens pStructType]+pType :: Parser ResType+pType =+ choice [try $ Scalar <$> pScalarType, pArrayType, parens pType] fromStringParse :: Parser a -> String -> String -> a fromStringParse p what s =@@ -184,11 +192,17 @@ onError e = error $ "not a " <> what <> ": " <> s <> "\n" <> errorBundlePretty e -instance IsString (ScalarTypeBase Size ()) where- fromString = fromStringParse pScalarType "ScalarType"+instance IsString (ScalarTypeBase Size NoUniqueness) where+ fromString =+ fromStringParse (second (const NoUniqueness) <$> pScalarType) "ScalarType" instance IsString StructType where- fromString = fromStringParse pStructType "StructType"+ fromString =+ fromStringParse (second (const NoUniqueness) <$> pType) "StructType" instance IsString StructRetType where- fromString = fromStringParse pStructRetType "StructRetType"+ fromString =+ fromStringParse (second (pure NoUniqueness) <$> pRetType) "StructRetType"++instance IsString ResRetType where+ fromString = fromStringParse pRetType "ResRetType"
unittests/Language/Futhark/TypeChecker/TypesTests.hs view
@@ -1,6 +1,6 @@ module Language.Futhark.TypeChecker.TypesTests (tests) where -import Data.Bifunctor (first)+import Data.Bifunctor import Data.List (isInfixOf) import Data.Map qualified as M import Data.Text qualified as T@@ -11,11 +11,12 @@ import Language.Futhark.SyntaxTests () import Language.Futhark.TypeChecker (initialEnv) import Language.Futhark.TypeChecker.Monad+import Language.Futhark.TypeChecker.Terms import Language.Futhark.TypeChecker.Types import Test.Tasty import Test.Tasty.HUnit -evalTest :: TypeExp NoInfo Name -> Either String ([VName], StructRetType) -> TestTree+evalTest :: TypeExp NoInfo Name -> Either String ([VName], ResRetType) -> TestTree evalTest te expected = testCase (prettyString te) $ case (fmap (extract . fst) (run (checkTypeExp te)), expected) of@@ -31,9 +32,9 @@ assertFailure $ "Expected error, got: " <> show actual_t where extract (_, svars, t, _) = (svars, t)- run = snd . runTypeM env mempty (mkInitialImport "") blankNameSource+ run = snd . runTypeM env mempty (mkInitialImport "") blankNameSource checkSizeExp -- We hack up an environment with some predefined type- -- abbreviations for testing. This is all prettyString sensitive to the+ -- abbreviations for testing. This is all pretty sensitive to the -- specific unique names, so we have to be careful! env = initialEnv
unittests/futhark_tests.hs view
@@ -7,6 +7,7 @@ import Futhark.IR.Mem.IxFunTests qualified import Futhark.IR.PropTests qualified import Futhark.IR.Syntax.CoreTests qualified+import Futhark.Internalise.TypesValuesTests qualified import Futhark.Optimise.MemoryBlockMerging.GreedyColoringTests qualified import Futhark.Pkg.SolveTests qualified import Language.Futhark.PrimitiveTests qualified@@ -24,6 +25,7 @@ Futhark.IR.PropTests.tests, Futhark.IR.Syntax.CoreTests.tests, Futhark.Pkg.SolveTests.tests,+ Futhark.Internalise.TypesValuesTests.tests, Futhark.IR.Mem.IntervalTests.tests, Futhark.IR.Mem.IxFunTests.tests, Language.Futhark.PrimitiveTests.tests,