diff --git a/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Alpha.hs b/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Alpha.hs index b2b04e0896e9..28be0276a42d 100644 --- a/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Alpha.hs +++ b/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Alpha.hs @@ -5,6 +5,11 @@ module DA.Daml.LF.Ast.Alpha ( alphaType , alphaExpr + , alphaType' + , initialAlphaEnv + , alphaTypeCon + , bindTypeVar + , AlphaEnv(..) ) where import qualified Data.Map.Strict as Map @@ -27,6 +32,10 @@ data AlphaEnv = AlphaEnv , boundExprVarsRhs :: !(Map.Map ExprVarName Int) -- ^ Maps bound expr variables from the right-hand-side to -- the depth of the binder which introduced them. + , tconEquivalence :: !(Qualified TypeConName -> Qualified TypeConName -> Bool) + -- ^ Defines how names in typecons should be compared + -- Unlike above fields, this should not mutate over the course of the alpha + -- equivalence check } onList :: (a -> a -> Bool) -> [a] -> [a] -> Bool @@ -77,7 +86,7 @@ alphaType' env = \case TVar x2 -> alphaTypeVar env x1 x2 _ -> False TCon c1 -> \case - TCon c2 -> alphaTypeCon c1 c2 + TCon c2 -> tconEquivalence env c1 c2 _ -> False TApp t1a t1b -> \case TApp t2a t2b -> alphaType' env t1a t2a && alphaType' env t1b t2b @@ -475,6 +484,7 @@ initialAlphaEnv = AlphaEnv , boundTypeVarsRhs = Map.empty , boundExprVarsLhs = Map.empty , boundExprVarsRhs = Map.empty + , tconEquivalence = alphaTypeCon } alphaType :: Type -> Type -> Bool diff --git a/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Util.hs b/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Util.hs index d8853faa83dd..92cb028ab798 100644 --- a/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Util.hs +++ b/sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Util.hs @@ -1,26 +1,33 @@ -- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE TemplateHaskell #-} module DA.Daml.LF.Ast.Util(module DA.Daml.LF.Ast.Util) where +import Control.DeepSeq +import Control.Lens +import Control.Lens.Ast import Control.Monad import Data.List import Data.Maybe import qualified Data.Text as T -import Control.Lens -import Control.Lens.Ast +import Data.Data import Data.Functor.Foldable import qualified Data.Graph as G import Data.List.Extra (nubSort, stripInfixEnd) import qualified Data.NameMap as NM +import GHC.Generics (Generic) import Module (UnitId, unitIdString, stringToUnitId) import System.FilePath +import Text.Read (readMaybe) import DA.Daml.LF.Ast.Base import DA.Daml.LF.Ast.TypeLevelNat import DA.Daml.LF.Ast.Optics import DA.Daml.LF.Ast.Recursive +import DA.Daml.LF.Ast.Version dvalName :: DefValue -> ExprValName dvalName = fst . dvalBinder @@ -31,6 +38,25 @@ dvalType = snd . dvalBinder chcArgType :: TemplateChoice -> Type chcArgType = snd . chcArgBinder +-- Return topologically sorted packages, with the top-level parent package first +topoSortPackages :: [(PackageId, a, Package)] -> Either [(PackageId, a, Package)] [(PackageId, a, Package)] +topoSortPackages pkgs = + let toPkgNode x@(pkgId, _, pkg) = + ( x + , pkgId + , toListOf (packageRefs . _PRImport) pkg + ) + fromPkgNode (x, _pkgId, _deps) = x + sccs = G.stronglyConnCompR (map toPkgNode pkgs) + isAcyclic = \case + G.AcyclicSCC pkg -> Right pkg + -- A package referencing itself shouldn't happen, but is not an actually + -- problematic cycle and won't trip up the engine + G.CyclicSCC [pkg] -> Right pkg + G.CyclicSCC pkgCycle -> Left (map fromPkgNode pkgCycle) + in + map fromPkgNode <$> traverse isAcyclic sccs + topoSortPackage :: Package -> Either [ModuleName] Package topoSortPackage pkg@Package{packageModules = mods} = do let isLocal (pkgRef, modName) = case pkgRef of @@ -47,6 +73,20 @@ topoSortPackage pkg@Package{packageModules = mods} = do mods <- traverse isAcyclic sccs pure pkg { packageModules = NM.fromList mods } +isUtilityPackage :: Package -> Bool +isUtilityPackage pkg = + all (\mod -> + null (moduleTemplates mod) + && null (moduleInterfaces mod) + && not (any (getIsSerializable . dataSerializable) $ moduleDataTypes mod) + ) $ packageModules pkg + + +pkgSupportsUpgrades :: Package -> Bool +pkgSupportsUpgrades pkg = + not (isUtilityPackage pkg) && + packageLfVersion pkg `supports` featurePackageUpgrades + data Arg = TmArg Expr | TyArg Type @@ -327,3 +367,62 @@ splitUnitId (unitIdString -> unitId) = fromMaybe (PackageName (T.pack unitId), N (name, ver) <- stripInfixEnd "-" unitId guard $ all (`elem` '.' : ['0' .. '9']) ver pure (PackageName (T.pack name), Just (PackageVersion (T.pack ver))) + +-- | Take a package version of regex "(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*" into +-- a list of integers [Integer] +splitPackageVersion + :: (PackageVersion -> a) -> PackageVersion + -> Either a RawPackageVersion +splitPackageVersion mkError version@(PackageVersion raw) = + let pieces = T.split (== '.') raw + in + case traverse (readMaybe . T.unpack) pieces of + Nothing -> Left (mkError version) + Just versions -> Right $ RawPackageVersion versions + +newtype RawPackageVersion = RawPackageVersion [Integer] + +padEquivalent :: RawPackageVersion -> RawPackageVersion -> ([Integer], [Integer]) +padEquivalent (RawPackageVersion v1Pieces) (RawPackageVersion v2Pieces) = + let pad xs target = + take + (length target `max` length xs) + (xs ++ repeat 0) + in + (pad v1Pieces v2Pieces, pad v2Pieces v1Pieces) + +instance Ord RawPackageVersion where + compare v1 v2 = uncurry compare $ padEquivalent v1 v2 + +instance Eq RawPackageVersion where + (==) v1 v2 = uncurry (==) $ padEquivalent v1 v2 + +instance Show RawPackageVersion where + show (RawPackageVersion pieces) = intercalate "." $ map show pieces + +data Upgrading a = Upgrading + { _past :: a + , _present :: a + } + deriving (Eq, Data, Generic, NFData, Show) + +makeLenses ''Upgrading + +instance Functor Upgrading where + fmap f Upgrading{..} = Upgrading (f _past) (f _present) + +instance Foldable Upgrading where + foldMap f Upgrading{..} = f _past <> f _present + +instance Traversable Upgrading where + traverse f Upgrading{..} = Upgrading <$> f _past <*> f _present + +instance Applicative Upgrading where + pure a = Upgrading a a + (<*>) f a = Upgrading { _past = _past f (_past a), _present = _present f (_present a) } + +foldU :: (a -> a -> b) -> Upgrading a -> b +foldU f u = f (_past u) (_present u) + +unsafeZipUpgrading :: Upgrading [a] -> [Upgrading a] +unsafeZipUpgrading = foldU (zipWith Upgrading) diff --git a/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Env.hs b/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Env.hs index 2dab97d87be1..c08215035edc 100644 --- a/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Env.hs +++ b/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Env.hs @@ -179,7 +179,14 @@ warnWithContextF :: forall m gamma. MonadGammaF gamma m => Getter gamma Gamma -> warnWithContextF = diagnosticWithContextF withContextF :: MonadGammaF gamma m => Setter' gamma Gamma -> Context -> m b -> m b -withContextF setter ctx = local (set (setter . locCtx) ctx) +withContextF setter newCtx = local (over (setter . locCtx) setCtx) + where + setCtx :: Context -> Context + setCtx oldCtx = + case (oldCtx, newCtx) of + (ContextDefUpgrading {}, ContextDefUpgrading {}) -> newCtx + (ContextDefUpgrading { cduPkgName, cduPkgVersion, cduIsDependency }, _) -> ContextDefUpgrading cduPkgName cduPkgVersion newCtx cduIsDependency + (_, _) -> newCtx instance SomeErrorOrWarning UnwarnableError where diagnosticWithContextF = throwWithContextF diff --git a/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Error.hs b/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Error.hs index 1d18f3f639d5..391082529f4f 100644 --- a/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Error.hs +++ b/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Error.hs @@ -15,6 +15,7 @@ module DA.Daml.LF.TypeChecker.Error( errorLocation, toDiagnostic, Warning(..), + PackageUpgradeOrigin(..), ) where import Control.Applicative @@ -39,6 +40,12 @@ data Context | ContextDefValue !Module !DefValue | ContextDefException !Module !DefException | ContextDefInterface !Module !DefInterface !InterfacePart + | ContextDefUpgrading + { cduPkgName :: !PackageName -- Name of package being checked for upgrade validity + , cduPkgVersion :: !(Upgrading RawPackageVersion) -- Prior (upgradee) and current (upgrader) version of dep package + , cduSubContext :: !Context -- Context within the package of the error + , cduIsDependency :: !Bool -- Is the package a dependency package or is it the main package? + } data TemplatePart = TPWhole @@ -204,12 +211,16 @@ data UnwarnableError | EUpgradeTemplateAddedKey !TypeConName !TemplateKey | EUpgradeTriedToUpgradeIface !TypeConName | EUpgradeMissingImplementation !TypeConName !TypeConName + | EForbiddenNewImplementation !TypeConName !TypeConName + | EUpgradeDependenciesFormACycle ![(PackageId, Maybe PackageMetadata)] deriving (Show) data WarnableError = WEUpgradeShouldDefineIfacesAndTemplatesSeparately | WEUpgradeShouldDefineIfaceWithoutImplementation !TypeConName ![TypeConName] | WEUpgradeShouldDefineTplInSeparatePackage !TypeConName !TypeConName + | WEDependencyHasUnparseableVersion !PackageName !PackageVersion !PackageUpgradeOrigin + | WEDependencyHasNoMetadataDespiteUpgradeability !PackageId !PackageUpgradeOrigin deriving (Show) instance Pretty WarnableError where @@ -235,6 +246,18 @@ instance Pretty WarnableError where , "It is recommended that interfaces are defined in their own package separate from their implementations." , "Ignore this error message with the --warn-bad-interface-instances=yes flag." ] + WEDependencyHasUnparseableVersion pkgName version packageOrigin -> + "Dependency " <> pPrint pkgName <> " of " <> pPrint packageOrigin <> " has a version which cannot be parsed: '" <> pPrint version <> "'" + WEDependencyHasNoMetadataDespiteUpgradeability pkgId packageOrigin -> + "Dependency with package ID " <> pPrint pkgId <> " of " <> pPrint packageOrigin <> " has no metadata, despite being compiled with an SDK version that supports metadata." + +data PackageUpgradeOrigin = UpgradingPackage | UpgradedPackage + deriving (Eq, Ord, Show) + +instance Pretty PackageUpgradeOrigin where + pPrint = \case + UpgradingPackage -> "upgrading package" + UpgradedPackage -> "upgraded package" data UpgradedRecordOrigin = TemplateBody TypeConName @@ -254,6 +277,7 @@ contextLocation = \case ContextDefValue _ v -> dvalLocation v ContextDefException _ e -> exnLocation e ContextDefInterface _ i ip -> interfaceLocation i ip <|> intLocation i -- Fallback to interface header location if other locations are missing + ContextDefUpgrading {} -> Nothing templateLocation :: Template -> TemplatePart -> Maybe SourceLoc templateLocation t = \case @@ -304,6 +328,13 @@ instance Show Context where "exception " <> show (moduleName m) <> "." <> show (exnName e) ContextDefInterface m i p -> "interface " <> show (moduleName m) <> "." <> show (intName i) <> " " <> show p + ContextDefUpgrading { cduPkgName, cduPkgVersion, cduSubContext, cduIsDependency } -> + let prettyPkgName = + if cduIsDependency + then "dependency " <> T.unpack (unPackageName cduPkgName) + else T.unpack (unPackageName cduPkgName) + in + "upgrading " <> prettyPkgName <> " " <> show (_present cduPkgVersion) <> ", " <> show cduSubContext instance Show TemplatePart where show = \case @@ -368,11 +399,7 @@ instance Pretty Error where EUnwarnableError err -> pPrint err EWarnableError err -> pPrint err EWarningToError warning -> pPrint warning - EContext ctx err -> - vcat - [ "error type checking " <> pretty ctx <> ":" - , nest 2 (pretty err) - ] + EContext ctx err -> prettyWithContext ctx (Right err) instance Pretty UnwarnableError where pPrint = \case @@ -650,6 +677,15 @@ instance Pretty UnwarnableError where EUpgradeTemplateAddedKey template _key -> "The upgraded template " <> pPrint template <> " cannot add a key where it didn't have one previously." EUpgradeTriedToUpgradeIface iface -> "Tried to upgrade interface " <> pPrint iface <> ", but interfaces cannot be upgraded. They should be removed in any upgrading package." EUpgradeMissingImplementation tpl iface -> "Implementation of interface " <> pPrint iface <> " by template " <> pPrint tpl <> " appears in package that is being upgraded, but does not appear in this package." + EForbiddenNewImplementation tpl iface -> "Implementation of interface " <> pPrint iface <> " by template " <> pPrint tpl <> " appears in this package, but does not appear in package that is being upgraded." + EUpgradeDependenciesFormACycle deps -> + vcat + [ "Dependencies from the `upgrades:` field and dependencies defined on the current package form a cycle:" + , nest 2 $ vcat $ map pprintDep deps + ] + where + pprintDep (pkgId, Just meta) = pPrint pkgId <> "(" <> pPrint (packageName meta) <> ", " <> pPrint (packageVersion meta) <> ")" + pprintDep (pkgId, Nothing) = pPrint pkgId instance Pretty UpgradedRecordOrigin where pPrint = \case @@ -659,24 +695,42 @@ instance Pretty UpgradedRecordOrigin where InterfaceBody iface -> "interface " <> pPrint iface TopLevel datatype -> "data type " <> pPrint datatype -instance Pretty Context where - pPrint = \case +prettyWithContext :: Context -> Either Warning Error -> Doc a +prettyWithContext ctx warningOrErr = + let header prettyCtx = + vcat + [ case warningOrErr of + Right _ -> "error type checking " <> prettyCtx <> ":" + Left _ -> "warning while type checking " <> prettyCtx <> ":" + , nest 2 (either pretty pretty warningOrErr) + ] + in + case ctx of ContextNone -> - string "" + header $ string "" ContextDefModule m -> - hsep [ "module" , pretty (moduleName m) ] + header $ hsep [ "module" , pretty (moduleName m) ] ContextDefTypeSyn m ts -> - hsep [ "type synonym", pretty (moduleName m) <> "." <> pretty (synName ts) ] + header $ hsep [ "type synonym", pretty (moduleName m) <> "." <> pretty (synName ts) ] ContextDefDataType m dt -> - hsep [ "data type", pretty (moduleName m) <> "." <> pretty (dataTypeCon dt) ] + header $ hsep [ "data type", pretty (moduleName m) <> "." <> pretty (dataTypeCon dt) ] ContextTemplate m t p -> - hsep [ "template", pretty (moduleName m) <> "." <> pretty (tplTypeCon t), string (show p) ] + header $ hsep [ "template", pretty (moduleName m) <> "." <> pretty (tplTypeCon t), string (show p) ] ContextDefValue m v -> - hsep [ "value", pretty (moduleName m) <> "." <> pretty (fst $ dvalBinder v) ] + header $ hsep [ "value", pretty (moduleName m) <> "." <> pretty (fst $ dvalBinder v) ] ContextDefException m e -> - hsep [ "exception", pretty (moduleName m) <> "." <> pretty (exnName e) ] + header $ hsep [ "exception", pretty (moduleName m) <> "." <> pretty (exnName e) ] ContextDefInterface m i p -> - hsep [ "interface", pretty (moduleName m) <> "." <> pretty (intName i), string (show p)] + header $ hsep [ "interface", pretty (moduleName m) <> "." <> pretty (intName i), string (show p)] + ContextDefUpgrading { cduPkgName, cduPkgVersion, cduSubContext, cduIsDependency } -> + let prettyPkgName = if cduIsDependency then hsep ["dependency", pretty cduPkgName] else pretty cduPkgName + upgradeOrDowngrade = if _present cduPkgVersion > _past cduPkgVersion then "upgrade" else "downgrade" + in + vcat + [ hsep [ "error while validating that", prettyPkgName, "version", string (show (_present cduPkgVersion)), "is a valid", upgradeOrDowngrade, "of version", string (show (_past cduPkgVersion)) ] + , nest 2 $ + prettyWithContext cduSubContext warningOrErr + ] class ToDiagnostic a where toDiagnostic :: a -> Diagnostic @@ -728,11 +782,7 @@ warningLocation = \case instance Pretty Warning where pPrint = \case - WContext ctx err -> - vcat - [ "warning while type checking " <> pretty ctx <> ":" - , nest 2 (pretty err) - ] + WContext ctx warning -> prettyWithContext ctx (Left warning) WTemplateChangedPrecondition template -> "The upgraded template " <> pPrint template <> " has changed the definition of its precondition." WTemplateChangedSignatories template -> "The upgraded template " <> pPrint template <> " has changed the definition of its signatories." WTemplateChangedObservers template -> "The upgraded template " <> pPrint template <> " has changed the definition of its observers." diff --git a/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Upgrade.hs b/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Upgrade.hs index a9ed67297aae..0386a68a1b67 100644 --- a/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Upgrade.hs +++ b/sdk/compiler/daml-lf-tools/src/DA/Daml/LF/TypeChecker/Upgrade.hs @@ -8,18 +8,17 @@ module DA.Daml.LF.TypeChecker.Upgrade ( module DA.Daml.LF.TypeChecker.Upgrade ) where -import Control.DeepSeq -import Control.Monad (unless, forM_, when) +import Control.Monad (unless, forM, forM_, when) import Control.Monad.Reader (withReaderT) +import Control.Monad.Reader.Class (asks) import Control.Lens hiding (Context) import DA.Daml.LF.Ast as LF -import DA.Daml.LF.Ast.Alpha (alphaExpr, alphaType) +import DA.Daml.LF.Ast.Alpha (alphaExpr, AlphaEnv(..), initialAlphaEnv, alphaType', alphaTypeCon) import DA.Daml.LF.TypeChecker.Check (expandTypeSynonyms) import DA.Daml.LF.TypeChecker.Env import DA.Daml.LF.TypeChecker.Error import DA.Daml.Options.Types (UpgradeInfo (..)) import Data.Bifunctor (first) -import Data.Data import Data.Either (partitionEithers) import Data.Hashable import qualified Data.HashMap.Strict as HMS @@ -27,123 +26,262 @@ import Data.List (foldl') import qualified Data.NameMap as NM import qualified Data.Text as T import Development.IDE.Types.Diagnostics -import GHC.Generics (Generic) +import Data.Maybe (catMaybes) +import Safe (maximumByMay, minimumByMay) +import Data.Function (on) -data Upgrading a = Upgrading - { _past :: a - , _present :: a - } - deriving (Eq, Data, Generic, NFData, Show) - -makeLenses ''Upgrading - -instance Functor Upgrading where - fmap f Upgrading{..} = Upgrading (f _past) (f _present) - -instance Foldable Upgrading where - foldMap f Upgrading{..} = f _past <> f _present +-- Allows us to split the world into upgraded and non-upgraded +type TcUpgradeM = TcMF UpgradingEnv +type TcPreUpgradeM = TcMF (Version, UpgradeInfo) -instance Traversable Upgrading where - traverse f Upgrading{..} = Upgrading <$> f _past <*> f _present +type UpgradingDeps = HMS.HashMap LF.PackageId (LF.PackageName, RawPackageVersion) -instance Applicative Upgrading where - pure a = Upgrading a a - (<*>) f a = Upgrading { _past = _past f (_past a), _present = _present f (_present a) } +data UpgradingEnv = UpgradingEnv + { _upgradingGamma :: Upgrading Gamma + , _upgradingDeps :: UpgradingDeps + } -foldU :: (a -> a -> b) -> Upgrading a -> b -foldU f u = f (_past u) (_present u) +makeLenses ''UpgradingEnv --- Allows us to split the world into upgraded and non-upgraded -type TcUpgradeM = TcMF (Upgrading Gamma) +present' :: Functor f => (Gamma -> f Gamma) -> UpgradingEnv -> f UpgradingEnv +present' = upgradingGamma . present runGammaUnderUpgrades :: Upgrading (TcM a) -> TcUpgradeM (Upgrading a) runGammaUnderUpgrades Upgrading{ _past = pastAction, _present = presentAction } = do - pastResult <- withReaderT _past pastAction - presentResult <- withReaderT _present presentAction + pastResult <- withReaderT (_past . _upgradingGamma) pastAction + presentResult <- withReaderT (_present . _upgradingGamma) presentAction pure Upgrading { _past = pastResult, _present = presentResult } -checkBothAndSingle - :: World -> (LF.UpgradedPackageId -> LF.Package -> TcUpgradeM ()) -> TcM () - -> Version -> UpgradeInfo - -> Maybe (LF.PackageId, LF.Package) - -> [Diagnostic] -checkBothAndSingle world checkBoth checkSingle version upgradeInfo mbUpgradedPackage = - let shouldTypecheck = version `LF.supports` LF.featurePackageUpgrades && uiTypecheckUpgrades upgradeInfo - - gamma :: World -> Gamma - gamma world = - let addBadIfaceSwapIndicator :: Gamma -> Gamma - addBadIfaceSwapIndicator = - if uiWarnBadInterfaceInstances upgradeInfo - then - addDiagnosticSwapIndicator (\case - Left WEUpgradeShouldDefineIfaceWithoutImplementation {} -> Just True - Left WEUpgradeShouldDefineTplInSeparatePackage {} -> Just True - Left WEUpgradeShouldDefineIfacesAndTemplatesSeparately {} -> Just True - _ -> Nothing) - else id - in - addBadIfaceSwapIndicator $ emptyGamma world version - - bothPkgDiagnostics :: Either Error ((), [Warning]) - bothPkgDiagnostics = - case mbUpgradedPackage of - Nothing -> - Right ((), []) - Just (pastPkgId, pastPkg) -> - let upgradingWorld = Upgrading { _past = initWorldSelf [] pastPkg, _present = world } - upgradingGamma = fmap gamma upgradingWorld - in - runGammaF upgradingGamma $ - when shouldTypecheck (checkBoth (UpgradedPackageId pastPkgId) pastPkg) - - singlePkgDiagnostics :: Either Error ((), [Warning]) - singlePkgDiagnostics = - runGammaF (gamma world) $ - when shouldTypecheck checkSingle - - extractDiagnostics :: Either Error ((), [Warning]) -> [Diagnostic] - extractDiagnostics result = - case result of - Left err -> [toDiagnostic err] - Right ((), warnings) -> map toDiagnostic warnings +shouldTypecheck :: Version -> UpgradeInfo -> Bool +shouldTypecheck version upgradeInfo = version `LF.supports` LF.featurePackageUpgrades && uiTypecheckUpgrades upgradeInfo + +shouldTypecheckM :: TcPreUpgradeM Bool +shouldTypecheckM = asks (uncurry shouldTypecheck) + +mkGamma :: Version -> UpgradeInfo -> World -> Gamma +mkGamma version upgradeInfo world = + let addBadIfaceSwapIndicator :: Gamma -> Gamma + addBadIfaceSwapIndicator = + if uiWarnBadInterfaceInstances upgradeInfo + then + addDiagnosticSwapIndicator (\case + Left WEUpgradeShouldDefineIfaceWithoutImplementation {} -> Just True + Left WEUpgradeShouldDefineTplInSeparatePackage {} -> Just True + Left WEUpgradeShouldDefineIfacesAndTemplatesSeparately {} -> Just True + _ -> Nothing) + else id in - extractDiagnostics bothPkgDiagnostics ++ extractDiagnostics singlePkgDiagnostics + addBadIfaceSwapIndicator $ emptyGamma world version + +gammaM :: World -> TcPreUpgradeM Gamma +gammaM world = asks (flip (uncurry mkGamma) world) + +extractDiagnostics :: Version -> UpgradeInfo -> TcPreUpgradeM () -> [Diagnostic] +extractDiagnostics version upgradeInfo action = + case runGammaF (version, upgradeInfo) action of + Left err -> [toDiagnostic err] + Right ((), warnings) -> map toDiagnostic warnings checkUpgrade :: LF.Package - -> Version -> UpgradeInfo - -> Maybe (LF.PackageId, LF.Package) + -> [LF.DalfPackage] -> Version -> UpgradeInfo + -> Maybe ((LF.PackageId, LF.Package), [(LF.PackageId, LF.Package)]) -> [Diagnostic] -checkUpgrade pkg = - let world = initWorldSelf [] pkg - checkBoth upgradedPkgId upgradedPkg = - checkUpgradeM upgradedPkgId (Upgrading upgradedPkg pkg) - checkSingle = do - checkNewInterfacesAreUnused pkg - checkNewInterfacesHaveNoTemplates pkg - in - checkBothAndSingle world checkBoth checkSingle +checkUpgrade pkg deps version upgradeInfo mbUpgradedPkg = + extractDiagnostics version upgradeInfo $ do + shouldTypecheck <- shouldTypecheckM + when shouldTypecheck $ do + case mbUpgradedPkg of + Nothing -> pure () + Just (upgradedPkg, upgradingDeps) -> do + deps <- checkUpgradeDependenciesM deps (upgradedPkg : upgradingDeps) + checkUpgradeBoth Nothing pkg (upgradedPkg, deps) + checkUpgradeSingle Nothing pkg + +checkUpgradeBoth :: Maybe Context -> LF.Package -> ((LF.PackageId, LF.Package), UpgradingDeps) -> TcPreUpgradeM () +checkUpgradeBoth mbContext pkg ((upgradedPkgId, upgradedPkg), upgradingDeps) = + let presentWorld = initWorldSelf [] pkg + pastWorld = initWorldSelf [] upgradedPkg + upgradingWorld = Upgrading { _past = pastWorld, _present = presentWorld } + withMbContext :: TcUpgradeM () -> TcUpgradeM () + withMbContext = + case mbContext of + Nothing -> id + Just context -> withContextF present' context + in + withReaderT (\(version, upgradeInfo) -> UpgradingEnv (mkGamma version upgradeInfo <$> upgradingWorld) upgradingDeps) $ + withMbContext $ + checkUpgradeM (UpgradedPackageId upgradedPkgId) (Upgrading upgradedPkg pkg) + +checkUpgradeSingle :: Maybe Context -> LF.Package -> TcPreUpgradeM () +checkUpgradeSingle mbContext pkg = + let presentWorld = initWorldSelf [] pkg + withMbContext :: TcM () -> TcM () + withMbContext = maybe id withContext mbContext + in + withReaderT (\(version, upgradeInfo) -> mkGamma version upgradeInfo presentWorld) $ + withMbContext $ do + checkNewInterfacesAreUnused pkg + checkNewInterfacesHaveNoTemplates pkg checkModule :: LF.World -> LF.Module - -> Version -> UpgradeInfo - -> Maybe (LF.PackageId, LF.Package) + -> [LF.DalfPackage] -> Version -> UpgradeInfo + -> Maybe ((LF.PackageId, LF.Package), [(LF.PackageId, LF.Package)]) -> [Diagnostic] -checkModule world0 module_ = - let world = extendWorldSelf module_ world0 - checkBoth upgradedPkgId upgradedPkg = - case NM.lookup (NM.name module_) (LF.packageModules upgradedPkg) of - Nothing -> pure () - Just pastModule -> - let upgradingModule = Upgrading { _past = pastModule, _present = module_ } - in - checkModuleM upgradedPkgId upgradingModule - checkSingle = do - checkNewInterfacesAreUnused module_ - checkNewInterfacesHaveNoTemplates module_ - in - checkBothAndSingle world checkBoth checkSingle +checkModule world0 module_ deps version upgradeInfo mbUpgradedPkg = + extractDiagnostics version upgradeInfo $ + when (shouldTypecheck version upgradeInfo) $ do + let world = extendWorldSelf module_ world0 + case mbUpgradedPkg of + Nothing -> pure () + Just (upgradedPkgWithId@(upgradedPkgIdRaw, upgradedPkg), upgradingDeps) -> do + let upgradedPkgId = UpgradedPackageId upgradedPkgIdRaw + -- TODO: https://github.com/digital-asset/daml/issues/19859 + deps <- checkUpgradeDependenciesM deps (upgradedPkgWithId : upgradingDeps) + let upgradingWorld = Upgrading { _past = initWorldSelf [] upgradedPkg, _present = world } + withReaderT (\(version, upgradeInfo) -> UpgradingEnv (mkGamma version upgradeInfo <$> upgradingWorld) deps) $ + case NM.lookup (NM.name module_) (LF.packageModules upgradedPkg) of + Nothing -> pure () + Just pastModule -> do + let upgradingModule = Upgrading { _past = pastModule, _present = module_ } + checkModuleM upgradedPkgId upgradingModule + withReaderT (\(version, upgradeInfo) -> mkGamma version upgradeInfo world) $ do + checkNewInterfacesAreUnused module_ + checkNewInterfacesHaveNoTemplates module_ + +checkUpgradeDependenciesM + :: [LF.DalfPackage] + -> [(LF.PackageId, LF.Package)] + -> TcPreUpgradeM UpgradingDeps +checkUpgradeDependenciesM presentDeps pastDeps = do + initialUpgradeablePackageMap <- + fmap (HMS.fromListWith (<>) . catMaybes) $ forM pastDeps $ \pastDep -> do + let (pkgId, pkg@LF.Package{packageMetadata = mbMeta}) = pastDep + withPkgAsGamma pkg $ do + if LF.pkgSupportsUpgrades pkg + then + case mbMeta of + Nothing -> do + diagnosticWithContext $ WErrorToWarning $ WEDependencyHasNoMetadataDespiteUpgradeability pkgId UpgradedPackage + pure Nothing + Just meta -> do + let LF.PackageMetadata {packageName, packageVersion} = meta + case splitPackageVersion id packageVersion of + Left version -> do + diagnosticWithContext $ WErrorToWarning $ WEDependencyHasUnparseableVersion packageName version UpgradedPackage + pure Nothing + Right rawVersion -> + pure $ Just (packageName, [(rawVersion, pkgId, pkg)]) + else pure Nothing + + let withIdAndPkg dalfPkg = (dalfPackageId dalfPkg, dalfPkg, extPackagePkg (dalfPackagePkg dalfPkg)) + withoutIdAndPkg (_, dalfPkg, _) = dalfPkg + + -- TODO: https://github.com/digital-asset/daml/issues/19859 + case topoSortPackages (map withIdAndPkg presentDeps) of + Left badTrace -> do + let placeholderPkg = let (_, _, pkg) = head badTrace in pkg + getPkgIdAndMetadata (pkgId, _, pkg) = (pkgId, packageMetadata pkg) + withPkgAsGamma placeholderPkg $ + throwWithContext $ EUpgradeDependenciesFormACycle $ map getPkgIdAndMetadata badTrace + Right sortedPresentDeps -> do + let dependenciesFirst = reverse (map withoutIdAndPkg sortedPresentDeps) + upgradeablePackageMap <- checkAllDeps initialUpgradeablePackageMap dependenciesFirst + pure $ upgradeablePackageMapToDeps upgradeablePackageMap + where + withPkgAsGamma pkg action = + withReaderT (\(version, _) -> emptyGamma (initWorldSelf [] pkg) version) action + + upgradeablePackageMapToDeps :: HMS.HashMap LF.PackageName [(LF.RawPackageVersion, LF.PackageId, LF.Package)] -> UpgradingDeps + upgradeablePackageMapToDeps upgradeablePackageMap = + HMS.fromList + [ (pkgId, (pkgName, pkgVersion)) + | (pkgName, versions) <- HMS.toList upgradeablePackageMap + , (pkgVersion, pkgId, _) <- versions + ] + + addDep + :: (LF.PackageName, (LF.RawPackageVersion, LF.PackageId, LF.Package)) + -> HMS.HashMap LF.PackageName [(LF.RawPackageVersion, LF.PackageId, LF.Package)] + -> HMS.HashMap LF.PackageName [(LF.RawPackageVersion, LF.PackageId, LF.Package)] + addDep (name, pkgVersionIdAndAst) upgradeablePackageMap = + HMS.insertWith (<>) name [pkgVersionIdAndAst] upgradeablePackageMap + + checkAllDeps + :: HMS.HashMap LF.PackageName [(LF.RawPackageVersion, LF.PackageId, LF.Package)] + -> [LF.DalfPackage] + -> TcPreUpgradeM (HMS.HashMap LF.PackageName [(LF.RawPackageVersion, LF.PackageId, LF.Package)]) + checkAllDeps upgradeablePackageMap [] = pure upgradeablePackageMap + checkAllDeps upgradeablePackageMap (pkg:rest) = do + mbNewDep <- checkOneDep upgradeablePackageMap pkg + let newUpgradeablePackageMap = + case mbNewDep of + Nothing -> upgradeablePackageMap + Just res -> addDep res upgradeablePackageMap + checkAllDeps newUpgradeablePackageMap rest + + checkOneDep + :: HMS.HashMap LF.PackageName [(LF.RawPackageVersion, LF.PackageId, LF.Package)] + -> LF.DalfPackage + -> TcPreUpgradeM (Maybe (LF.PackageName, (LF.RawPackageVersion, LF.PackageId, LF.Package))) + checkOneDep upgradeablePackageMap dalfPkg = do + let LF.DalfPackage{dalfPackagePkg,dalfPackageId=presentPkgId} = dalfPkg + presentPkg = extPackagePkg dalfPackagePkg + if not (LF.pkgSupportsUpgrades presentPkg) + then pure Nothing + else + case packageMetadata presentPkg of + Just meta -> + let PackageMetadata {packageName, packageVersion} = meta + in + case splitPackageVersion id packageVersion of + Left version -> do + withPkgAsGamma presentPkg $ + diagnosticWithContext $ WErrorToWarning $ WEDependencyHasUnparseableVersion packageName version UpgradedPackage + pure Nothing + Right presentVersion -> do + let result = (packageName, (presentVersion, presentPkgId, presentPkg)) + case HMS.lookup packageName upgradeablePackageMap of + Nothing -> pure () + Just upgradedPkgs -> do + let equivalent = filter (\(pastVersion, pastPkgId, _) -> pastVersion == presentVersion && pastPkgId /= presentPkgId) upgradedPkgs + ordFst = compare `on` (\(v,_,_) -> v) + closestGreater = minimumByMay ordFst $ filter (\(pastVersion, _, _) -> pastVersion > presentVersion) upgradedPkgs + closestLesser = maximumByMay ordFst $ filter (\(pastVersion, _, _) -> pastVersion < presentVersion) upgradedPkgs + if not (null equivalent) + then error "two upgradeable packages with same name and version" + else do + let otherDepsWithSelf = upgradeablePackageMapToDeps $ addDep result upgradeablePackageMap + case closestGreater of + Just (greaterPkgVersion, _greaterPkgId, greaterPkg) -> do + let context = ContextDefUpgrading { cduPkgName = packageName, cduPkgVersion = Upgrading greaterPkgVersion presentVersion, cduSubContext = ContextNone, cduIsDependency = True } + checkUpgradeBoth + (Just context) + greaterPkg + ((presentPkgId, presentPkg), otherDepsWithSelf) + checkUpgradeSingle + (Just context) + presentPkg + Nothing -> + pure () + case closestLesser of + Just (lesserPkgVersion, lesserPkgId, lesserPkg) -> do + let context = ContextDefUpgrading { cduPkgName = packageName, cduPkgVersion = Upgrading lesserPkgVersion presentVersion, cduSubContext = ContextNone, cduIsDependency = True } + checkUpgradeBoth + (Just context) + presentPkg + ((lesserPkgId, lesserPkg), otherDepsWithSelf) + checkUpgradeSingle + (Just context) + presentPkg + Nothing -> + pure () + pure (Just result) + Nothing -> do + withPkgAsGamma presentPkg $ + diagnosticWithContext $ WErrorToWarning $ WEDependencyHasNoMetadataDespiteUpgradeability presentPkgId UpgradingPackage + pure Nothing checkUpgradeM :: LF.UpgradedPackageId -> Upgrading LF.Package -> TcUpgradeM () checkUpgradeM upgradedPackageId package = do @@ -198,17 +336,17 @@ throwIfNonEmpty handleError hm = ctxHandler = case ctx of Nothing -> id - Just ctx -> withContextF present ctx + Just ctx -> withContextF present' ctx in - ctxHandler $ diagnosticWithContextF present err + ctxHandler $ diagnosticWithContextF present' err _ -> pure () checkModuleM :: LF.UpgradedPackageId -> Upgrading LF.Module -> TcUpgradeM () checkModuleM upgradedPackageId module_ = do - (existingTemplates, _new) <- checkDeleted (EUpgradeMissingTemplate . NM.name) $ NM.toHashMap . moduleTemplates <$> module_ + (existingTemplates, newTemplates) <- checkDeleted (EUpgradeMissingTemplate . NM.name) $ NM.toHashMap . moduleTemplates <$> module_ forM_ existingTemplates $ \template -> withContextF - present + present' (ContextTemplate (_present module_) (_present template) TPWhole) (checkTemplate module_ template) @@ -293,13 +431,11 @@ checkModuleM upgradedPackageId module_ = do | template <- NM.elems (moduleTemplates module_) , implementation <- NM.elems (tplImplements template) ] - (_instanceExisting, instanceNew) <- - checkDeletedWithContext - (\(tpl, impl) -> - ( ContextTemplate (_present module_) tpl TPWhole - , EUpgradeMissingImplementation (NM.name tpl) (LF.qualObject (NM.name impl)) - )) - (flattenInstances <$> module_) + let (instanceDel, _instanceExisting, instanceNew) = extractDelExistNew (flattenInstances <$> module_) + let notANewTemplate (tyCon, _) _ = not (HMS.member tyCon newTemplates) + checkDeletedInstances (_present module_) instanceDel + checkAddedInstances (_present module_) (HMS.filterWithKey notANewTemplate instanceNew) + checkUpgradedInterfacesAreUnused upgradedPackageId (_present module_) instanceNew -- checkDeleted should only trigger on datatypes not belonging to templates or choices or interfaces, which we checked above @@ -311,11 +447,37 @@ checkModuleM upgradedPackageId module_ = do in -- If origins don't match, record has changed origin if foldU (/=) (fst <$> origin) then - withContextF present (ContextDefDataType (_present module_) (_present dt)) $ - throwWithContextF present (EUpgradeRecordChangedOrigin (dataTypeCon (_present dt)) (fst (_past origin)) (fst (_present origin))) + withContextF present' (ContextDefDataType (_present module_) (_present dt)) $ + throwWithContextF present' (EUpgradeRecordChangedOrigin (dataTypeCon (_present dt)) (fst (_past origin)) (fst (_present origin))) else do let (presentOrigin, context) = _present origin - withContextF present context $ checkDefDataType presentOrigin dt + withContextF present' context $ checkDefDataType presentOrigin dt + +checkDeletedInstances :: + Module -> + HMS.HashMap (TypeConName, Qualified TypeConName) (Template, TemplateImplements) -> + TcUpgradeM () +checkDeletedInstances module_ instances = throwIfNonEmpty handleError instances + where + handleError :: + (Template, TemplateImplements) -> (Maybe Context, UnwarnableError) + handleError (tpl, impl) = + ( Just (ContextTemplate module_ tpl TPWhole) + , EUpgradeMissingImplementation (NM.name tpl) (LF.qualObject (NM.name impl)) + ) + +checkAddedInstances :: + Module -> + HMS.HashMap (TypeConName, Qualified TypeConName) (Template, TemplateImplements) -> + TcUpgradeM () +checkAddedInstances module_ instances = throwIfNonEmpty handleError instances + where + handleError :: + (Template, TemplateImplements) -> (Maybe Context, UnwarnableError) + handleError (tpl, impl) = + ( Just (ContextTemplate module_ tpl TPWhole) + , EForbiddenNewImplementation (NM.name tpl) (LF.qualObject (NM.name impl)) + ) -- It is always invalid to keep an interface in an upgrade checkContinuedIfaces @@ -326,8 +488,8 @@ checkContinuedIfaces module_ ifaces = forM_ ifaces $ \upgradedDtIface -> let (_dt, iface) = _present upgradedDtIface in - withContextF present (ContextDefInterface (_present module_) iface IPWhole) $ - throwWithContextF present $ EUpgradeTriedToUpgradeIface (NM.name iface) + withContextF present' (ContextDefInterface (_present module_) iface IPWhole) $ + throwWithContextF present' $ EUpgradeTriedToUpgradeIface (NM.name iface) class HasModules a where getModules :: a -> NM.NameMap LF.Module @@ -393,8 +555,8 @@ checkUpgradedInterfacesAreUnused upgradedPackageId module_ newInstances = do let qualifiedTplName = Qualified PRSelf (moduleName module_) tplName ifaceInstanceHead = InterfaceInstanceHead ifaceName qualifiedTplName in - withContextF present (ContextTemplate module_ tpl (TPInterfaceInstance ifaceInstanceHead Nothing)) $ - diagnosticWithContextF present $ WEUpgradeShouldDefineTplInSeparatePackage (NM.name tpl) (LF.qualObject (NM.name implementation)) + withContextF present' (ContextTemplate module_ tpl (TPInterfaceInstance ifaceInstanceHead Nothing)) $ + diagnosticWithContextF present' $ WEUpgradeShouldDefineTplInSeparatePackage (NM.name tpl) (LF.qualObject (NM.name implementation)) where fromUpgradedPackage :: forall a. LF.Qualified a -> Bool fromUpgradedPackage identifier = @@ -414,12 +576,12 @@ checkTemplate module_ template = do -- Check that no choices have been removed (existingChoices, _existingNew) <- checkDeleted (EUpgradeMissingChoice . NM.name) $ NM.toHashMap . tplChoices <$> template forM_ existingChoices $ \choice -> do - withContextF present (ContextTemplate (_present module_) (_present template) (TPChoice (_present choice))) $ do - checkUpgradeType (fmap chcReturnType choice) + withContextF present' (ContextTemplate (_present module_) (_present template) (TPChoice (_present choice))) $ do + checkType (fmap chcReturnType choice) (EUpgradeChoiceChangedReturnType (NM.name (_present choice))) whenDifferent "controllers" (extractFuncFromFuncThisArg . chcControllers) choice $ - warnWithContextF present $ WChoiceChangedControllers $ NM.name $ _present choice + warnWithContextF present' $ WChoiceChangedControllers $ NM.name $ _present choice let observersErr = WChoiceChangedObservers $ NM.name $ _present choice case fmap (mapENilToNothing . chcObservers) choice of @@ -428,9 +590,9 @@ checkTemplate module_ template = do Upgrading { _past = Just _past, _present = Just _present } -> do whenDifferent "observers" extractFuncFromFuncThisArg (Upgrading _past _present) - (warnWithContextF present observersErr) + (warnWithContextF present' observersErr) _ -> do - warnWithContextF present observersErr + warnWithContextF present' observersErr let authorizersErr = WChoiceChangedAuthorizers $ NM.name $ _present choice case fmap (mapENilToNothing . chcAuthorizers) choice of @@ -438,49 +600,50 @@ checkTemplate module_ template = do Upgrading { _past = Just _past, _present = Just _present } -> whenDifferent "authorizers" extractFuncFromFuncThisArg (Upgrading _past _present) - (warnWithContextF present authorizersErr) - _ -> warnWithContextF present authorizersErr + (warnWithContextF present' authorizersErr) + _ -> warnWithContextF present' authorizersErr pure choice -- This check assumes that we encode signatories etc. on a template as -- $ this, where $ is a function that contains the -- actual definition. We resolve this function and check that it is -- identical. - withContextF present (ContextTemplate (_present module_) (_present template) TPPrecondition) $ + withContextF present' (ContextTemplate (_present module_) (_present template) TPPrecondition) $ whenDifferent "precondition" (extractFuncFromCaseFuncThis . tplPrecondition) template $ - warnWithContextF present $ WTemplateChangedPrecondition $ NM.name $ _present template - withContextF present (ContextTemplate (_present module_) (_present template) TPSignatories) $ + warnWithContextF present' $ WTemplateChangedPrecondition $ NM.name $ _present template + withContextF present' (ContextTemplate (_present module_) (_present template) TPSignatories) $ whenDifferent "signatories" (extractFuncFromFuncThis . tplSignatories) template $ - warnWithContextF present $ WTemplateChangedSignatories $ NM.name $ _present template - withContextF present (ContextTemplate (_present module_) (_present template) TPObservers) $ + warnWithContextF present' $ WTemplateChangedSignatories $ NM.name $ _present template + withContextF present' (ContextTemplate (_present module_) (_present template) TPObservers) $ whenDifferent "observers" (extractFuncFromFuncThis . tplObservers) template $ - warnWithContextF present $ WTemplateChangedObservers $ NM.name $ _present template - withContextF present (ContextTemplate (_present module_) (_present template) TPAgreement) $ + warnWithContextF present' $ WTemplateChangedObservers $ NM.name $ _present template + withContextF present' (ContextTemplate (_present module_) (_present template) TPAgreement) $ whenDifferent "agreement" (extractFuncFromFuncThis . tplAgreement) template $ - warnWithContextF present $ WTemplateChangedAgreement $ NM.name $ _present template + warnWithContextF present' $ WTemplateChangedAgreement $ NM.name $ _present template - withContextF present (ContextTemplate (_present module_) (_present template) TPKey) $ do + withContextF present' (ContextTemplate (_present module_) (_present template) TPKey) $ do case fmap tplKey template of Upgrading { _past = Nothing, _present = Nothing } -> do pure () Upgrading { _past = Just pastKey, _present = Just presentKey } -> do let tplKey = Upgrading pastKey presentKey - -- Key type musn't change - checkUpgradeType (fmap tplKeyType tplKey) - (EUpgradeTemplateChangedKeyType (NM.name (_present template))) + -- Key type must be a valid upgrade + iset <- isUpgradedType (fmap tplKeyType tplKey) + when (not iset) $ + diagnosticWithContextF present' (EUpgradeTemplateChangedKeyType (NM.name (_present template))) -- But expression for computing it may whenDifferent "key expression" (extractFuncFromFuncThis . tplKeyBody) tplKey - (warnWithContextF present $ WTemplateChangedKeyExpression $ NM.name $ _present template) + (warnWithContextF present' $ WTemplateChangedKeyExpression $ NM.name $ _present template) whenDifferent "key maintainers" (extractFuncFromTyAppNil . tplKeyMaintainers) tplKey - (warnWithContextF present $ WTemplateChangedKeyMaintainers $ NM.name $ _present template) + (warnWithContextF present' $ WTemplateChangedKeyMaintainers $ NM.name $ _present template) Upgrading { _past = Just pastKey, _present = Nothing } -> - throwWithContextF present $ EUpgradeTemplateRemovedKey (NM.name (_present template)) pastKey + throwWithContextF present' $ EUpgradeTemplateRemovedKey (NM.name (_present template)) pastKey Upgrading { _past = Nothing, _present = Just presentKey } -> - throwWithContextF present $ EUpgradeTemplateAddedKey (NM.name (_present template)) presentKey + throwWithContextF present' $ EUpgradeTemplateAddedKey (NM.name (_present template)) presentKey -- TODO: Check that return type of a choice is compatible pure () @@ -497,7 +660,7 @@ checkTemplate module_ template = do in case resolvedWithPossibleError of Left err -> - warnWithContextF present (WCouldNotExtractForUpgradeChecking (T.pack field) (Just (T.pack err))) + warnWithContextF present' (WCouldNotExtractForUpgradeChecking (T.pack field) (Just (T.pack err))) Right resolvedExprs -> let exprsMatch = foldU alphaExpr $ fmap removeLocations resolvedExprs in @@ -589,10 +752,10 @@ checkDefDataType origin datatype = do let upgrade = Upgrading{..} (existing, _new) <- checkDeleted (\_ -> EUpgradeVariantRemovedVariant origin) (fmap HMS.fromList upgrade) when (not $ and $ foldU (zipWith (==)) $ fmap (map fst) upgrade) $ - throwWithContextF present (EUpgradeVariantVariantsOrderChanged origin) - different <- filterHashMapM (fmap not . isSameType) existing + throwWithContextF present' (EUpgradeVariantVariantsOrderChanged origin) + different <- filterHashMapM (fmap not . isUpgradedType) existing when (not (null different)) $ - throwWithContextF present $ EUpgradeVariantChangedVariantType origin + throwWithContextF present' $ EUpgradeVariantChangedVariantType origin Upgrading { _past = DataEnum _past, _present = DataEnum _present } -> do let upgrade = Upgrading{..} (_, _new) <- @@ -600,11 +763,11 @@ checkDefDataType origin datatype = do (\_ -> EUpgradeEnumRemovedVariant origin) (fmap (HMS.fromList . map (,())) upgrade) when (not $ and $ foldU (zipWith (==)) upgrade) $ - throwWithContextF present (EUpgradeEnumVariantsOrderChanged origin) + throwWithContextF present' (EUpgradeEnumVariantsOrderChanged origin) Upgrading { _past = DataInterface {}, _present = DataInterface {} } -> pure () _ -> - throwWithContextF present (EUpgradeMismatchDataConsVariety (dataTypeCon (_past datatype)) (dataCons (_past datatype)) (dataCons (_present datatype))) + throwWithContextF present' (EUpgradeMismatchDataConsVariety (dataTypeCon (_past datatype)) (dataCons (_past datatype)) (dataCons (_present datatype))) filterHashMapM :: (Applicative m) => (a -> m Bool) -> HMS.HashMap k a -> m (HMS.HashMap k a) filterHashMapM pred t = @@ -614,47 +777,45 @@ checkFields :: UpgradedRecordOrigin -> Upgrading [(FieldName, Type)] -> TcUpgrad checkFields origin fields = do (existing, new) <- checkDeleted (\_ -> EUpgradeRecordFieldsMissing origin) (fmap HMS.fromList fields) -- If a field from the upgraded package has had its type changed - different <- filterHashMapM (fmap not . isSameType) existing + different <- filterHashMapM (fmap not . isUpgradedType) existing when (not (HMS.null different)) $ - throwWithContextF present (EUpgradeRecordFieldsExistingChanged origin) + throwWithContextF present' (EUpgradeRecordFieldsExistingChanged origin) when (not (all newFieldOptionalType new)) $ case origin of VariantConstructor{} -> - throwWithContextF present (EUpgradeVariantAddedVariantField origin) + throwWithContextF present' (EUpgradeVariantAddedVariantField origin) _ -> - throwWithContextF present (EUpgradeRecordFieldsNewNonOptional origin) + throwWithContextF present' (EUpgradeRecordFieldsNewNonOptional origin) -- If a new field has a non-optional type -- If the order of fields changed when (not $ and $ foldU (zipWith (==)) $ fmap (map fst) fields) $ - throwWithContextF present (EUpgradeRecordFieldsOrderChanged origin) + throwWithContextF present' (EUpgradeRecordFieldsOrderChanged origin) where newFieldOptionalType (TOptional _) = True newFieldOptionalType _ = False -- Check type upgradability -checkUpgradeType :: SomeErrorOrWarning e => Upgrading Type -> e -> TcUpgradeM () -checkUpgradeType type_ err = do - sameType <- isSameType type_ - unless sameType $ diagnosticWithContextF present err +checkType :: SomeErrorOrWarning e => Upgrading Type -> e -> TcUpgradeM () +checkType type_ err = do + sameType <- isUpgradedType type_ + unless sameType $ diagnosticWithContextF present' err -isSameType :: Upgrading Type -> TcUpgradeM Bool -isSameType type_ = do +isUpgradedType :: Upgrading Type -> TcUpgradeM Bool +isUpgradedType type_ = do expandedTypes <- runGammaUnderUpgrades (expandTypeSynonyms <$> type_) - let strippedIdentifiers = fmap unifyTypes expandedTypes - pure (foldU alphaType strippedIdentifiers) - -unifyIdentifier :: Qualified a -> Qualified a -unifyIdentifier q = q { qualPackage = PRSelf } - -unifyTypes :: Type -> Type -unifyTypes typ = - case typ of - TNat n -> TNat n - TSynApp n args -> TSynApp (unifyIdentifier n) (map unifyTypes args) - TVar n -> TVar n - TCon con -> TCon (unifyIdentifier con) - TBuiltin bt -> TBuiltin bt - TApp fun arg -> TApp (unifyTypes fun) (unifyTypes arg) - TForall v body -> TForall v (unifyTypes body) - TStruct fields -> TStruct ((map . fmap) unifyTypes fields) - + deps <- view upgradingDeps + let checkSelf = alphaTypeCon + checkImport pastT presentT = + case (qualPackage pastT, qualPackage presentT) of + (PRImport pastPkgId, PRImport presentPkgId) -> + pastPkgId == presentPkgId || + (case (pastPkgId `HMS.lookup` deps, presentPkgId `HMS.lookup` deps) of + (Just (pastName, pastVersion), Just (presentName, presentVersion)) -> pastName == presentName && pastVersion < presentVersion + _ -> False + ) && removePkgId pastT == removePkgId presentT + _ -> False + tconCheck pastT presentT = checkSelf pastT presentT || checkImport pastT presentT + pure $ foldU (alphaType' initialAlphaEnv { tconEquivalence = tconCheck }) expandedTypes + +removePkgId :: Qualified a -> Qualified a +removePkgId a = a { qualPackage = PRSelf } diff --git a/sdk/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs b/sdk/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs index ffa884ae3a00..624350a54a8c 100644 --- a/sdk/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs +++ b/sdk/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/Dar.hs @@ -137,15 +137,12 @@ buildDar service PackageConfigFields {..} ifDir dalfInput upgradeInfo = do let pMeta = LF.PackageMetadata { packageName = pName , packageVersion = fromMaybe (LF.PackageVersion "0.0.0") pVersion - , upgradedPackageId = LF.UpgradedPackageId . fst <$> mbUpgradedPackage + , upgradedPackageId = LF.UpgradedPackageId . fst . fst <$> mbUpgradedPackage } pkg <- case optShakeFiles opts of Nothing -> mergePkgs pMeta lfVersion . map fst <$> usesE GeneratePackage files Just _ -> generateSerializedPackage pName pVersion pMeta files - MaybeT $ - runDiagnosticCheck $ diagsToIdeResult (toNormalizedFilePath' pSrc) $ - Upgrade.checkUpgrade pkg lfVersion upgradeInfo mbUpgradedPackage MaybeT $ finalPackageCheck (toNormalizedFilePath' pSrc) pkg let pkgModuleNames = map (Ghc.mkModuleName . T.unpack) $ LF.packageModuleNames pkg @@ -163,6 +160,9 @@ buildDar service PackageConfigFields {..} ifDir dalfInput upgradeInfo = do Just _ -> pure $ Just [] -- get all dalf dependencies. dalfDependencies0 <- getDalfDependencies files + MaybeT $ + runDiagnosticCheck $ diagsToIdeResult (toNormalizedFilePath' pSrc) $ + Upgrade.checkUpgrade pkg (Map.elems dalfDependencies0) lfVersion upgradeInfo mbUpgradedPackage let dalfDependencies = [ (T.pack $ unitIdString unitId, LF.dalfPackageBytes pkg, LF.dalfPackageId pkg) | (unitId, pkg) <- Map.toList dalfDependencies0 diff --git a/sdk/compiler/damlc/daml-ide-core/src/Development/IDE/Core/Rules/Daml.hs b/sdk/compiler/damlc/daml-ide-core/src/Development/IDE/Core/Rules/Daml.hs index 0091e150ae2e..b2abf8c03de5 100644 --- a/sdk/compiler/damlc/daml-ide-core/src/Development/IDE/Core/Rules/Daml.hs +++ b/sdk/compiler/damlc/daml-ide-core/src/Development/IDE/Core/Rules/Daml.hs @@ -293,10 +293,11 @@ getExternalPackages file = do map LF.dalfPackagePkg (Map.elems pkgMap) <> map LF.dalfPackagePkg (Map.elems stablePackages) -- Generates and type checks the DALF for a module. -generateDalfRule :: Options -> Rules () +generateDalfRule :: SdkVersioned => Options -> Rules () generateDalfRule opts = define $ \GenerateDalf file -> do lfVersion <- getDamlLfVersion + mbDalfDependencies <- runMaybeT (getDalfDependencies [file]) WhnfPackage pkg <- use_ GeneratePackageDeps file pkgs <- getExternalPackages file let world = LF.initWorldSelf pkgs pkg @@ -307,7 +308,7 @@ generateDalfRule opts = Left err -> ([ideErrorPretty file err], Nothing) Right dalf -> let lfDiags = LF.checkModule world lfVersion dalf - upgradeDiags = Upgrade.checkModule world dalf lfVersion (optUpgradeInfo opts) upgradedPackage + upgradeDiags = Upgrade.checkModule world dalf (foldMap Map.elems mbDalfDependencies) lfVersion (optUpgradeInfo opts) upgradedPackage in second (dalf <$) (diagsToIdeResult file (lfDiags ++ upgradeDiags)) -- TODO Share code with typecheckModule in ghcide. The environment needs to be setup @@ -542,11 +543,18 @@ extractUpgradedPackageRule opts = do Nothing -> pure Nothing Just path -> use ExtractUpgradedPackageFile (toNormalizedFilePath' path) define $ \ExtractUpgradedPackageFile file -> do - ExtractedDar{edMain} <- liftIO $ extractDar (fromNormalizedFilePath file) - let bs = BSL.toStrict $ ZipArchive.fromEntry edMain - case Archive.decodeArchive Archive.DecodeAsMain bs of - Left _ -> pure ([ideErrorPretty file ("Could not decode file as a DAR." :: T.Text)], Nothing) - Right (pid, package) -> pure ([], Just (pid, package)) + ExtractedDar{edMain,edDalfs} <- liftIO $ extractDar (fromNormalizedFilePath file) + let bsMain = BSL.toStrict $ ZipArchive.fromEntry edMain + let bsDeps = BSL.toStrict . ZipArchive.fromEntry <$> edDalfs + let mainAndDeps :: Either Archive.ArchiveError ((LF.PackageId, LF.Package), [(LF.PackageId, LF.Package)]) + mainAndDeps = do + main <- Archive.decodeArchive Archive.DecodeAsMain bsMain + deps <- Archive.decodeArchive Archive.DecodeAsDependency `traverse` bsDeps + pure (main, deps) + let myThing = case mainAndDeps of + Left _ -> ([ideErrorPretty file ("Could not decode file as a DAR." :: T.Text)], Nothing) + Right mainAndDeps -> ([], Just mainAndDeps) + pure myThing readDalfPackage :: FilePath -> IO (Either FileDiagnostic LF.DalfPackage) readDalfPackage dalf = do diff --git a/sdk/compiler/damlc/daml-rule-types/src/Development/IDE/Core/RuleTypes/Daml.hs b/sdk/compiler/damlc/daml-rule-types/src/Development/IDE/Core/RuleTypes/Daml.hs index 58c8d86610ec..2cba6b34ca2e 100644 --- a/sdk/compiler/damlc/daml-rule-types/src/Development/IDE/Core/RuleTypes/Daml.hs +++ b/sdk/compiler/damlc/daml-rule-types/src/Development/IDE/Core/RuleTypes/Daml.hs @@ -318,7 +318,7 @@ instance Binary ExtractUpgradedPackage instance Hashable ExtractUpgradedPackage instance NFData ExtractUpgradedPackage -type instance RuleResult ExtractUpgradedPackage = Maybe (LF.PackageId, LF.Package) +type instance RuleResult ExtractUpgradedPackage = Maybe ((LF.PackageId, LF.Package), [(LF.PackageId, LF.Package)]) data ExtractUpgradedPackageFile = ExtractUpgradedPackageFile deriving (Eq, Show, Typeable, Generic) @@ -326,4 +326,4 @@ instance Binary ExtractUpgradedPackageFile instance Hashable ExtractUpgradedPackageFile instance NFData ExtractUpgradedPackageFile -type instance RuleResult ExtractUpgradedPackageFile = (LF.PackageId, LF.Package) +type instance RuleResult ExtractUpgradedPackageFile = ((LF.PackageId, LF.Package), [(LF.PackageId, LF.Package)]) diff --git a/sdk/compiler/damlc/tests/BUILD.bazel b/sdk/compiler/damlc/tests/BUILD.bazel index 7fae5edcdbf1..536d0840d28e 100644 --- a/sdk/compiler/damlc/tests/BUILD.bazel +++ b/sdk/compiler/damlc/tests/BUILD.bazel @@ -442,7 +442,6 @@ da_haskell_test( da_haskell_test( name = "upgrades", srcs = ["src/DA/Test/DamlcUpgrades.hs"], - compiler_flags = ["-Wno-unused-local-binds"], data = [ "//compiler/damlc", "//daml-script/daml:daml-script.dar", @@ -456,9 +455,13 @@ da_haskell_test( "//test-common:upgrades-FailsWhenATopLevelVariantAddsAFieldToAVariantsType-files", "//test-common:upgrades-FailsWhenATopLevelVariantAddsAVariant-files", "//test-common:upgrades-FailsWhenATopLevelVariantRemovesAVariant-files", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedSeparateDep-files", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedUpgradedPackage-files", "//test-common:upgrades-FailsWhenAnInstanceIsDropped-files", "//test-common:upgrades-FailsWhenAnInterfaceIsDefinedInAnUpgradingPackageWhenItWasAlreadyInThePriorPackage-files", "//test-common:upgrades-FailsWhenDatatypeChangesVariety-files", + "//test-common:upgrades-FailsWhenDependencyIsNotAValidUpgrade-files", + "//test-common:upgrades-FailsWhenDepsDowngradeVersionsWhileUsingDatatypes-files", "//test-common:upgrades-FailsWhenExistingFieldInTemplateChoiceIsChanged-files", "//test-common:upgrades-FailsWhenExistingFieldInTemplateIsChanged-files", "//test-common:upgrades-FailsWhenNewFieldIsAddedToTemplateChoiceWithoutOptionalType-files", @@ -470,6 +473,10 @@ da_haskell_test( "//test-common:upgrades-FailsWhenTemplateChoiceChangesItsReturnType-files", "//test-common:upgrades-FailsWhenTemplateRemovesKeyType-files", "//test-common:upgrades-FailsWhenTwoDeeplyNestedTypeSynonymsResolveToDifferentDatatypes-files", + "//test-common:upgrades-FailsWhenUpgradedFieldFromDifferentPackageName-dep-name1.dar", + "//test-common:upgrades-FailsWhenUpgradedFieldFromDifferentPackageName-dep-name2.dar", + "//test-common:upgrades-FailsWhenUpgradedFieldFromDifferentPackageName-files", + "//test-common:upgrades-FailsWhenUpgradedFieldPackagesAreNotUpgradable-files", "//test-common:upgrades-FailsWithSynonymReturnTypeChange-files", "//test-common:upgrades-FailsWithSynonymReturnTypeChangeInSeparatePackage-files", "//test-common:upgrades-MissingChoice-files", @@ -480,12 +487,15 @@ da_haskell_test( "//test-common:upgrades-SucceedWhenATopLevelEnumAddsAField-files", "//test-common:upgrades-SucceedsWhenATopLevelEnumChanges-files", "//test-common:upgrades-SucceedsWhenATopLevelRecordAddsAnOptionalFieldAtTheEnd-files", + "//test-common:upgrades-SucceedsWhenATopLevelRecordAddsAnOptionalFieldAtTheEnd-v1.dar", + "//test-common:upgrades-SucceedsWhenATopLevelRecordAddsAnOptionalFieldAtTheEnd-v2.dar", "//test-common:upgrades-SucceedsWhenATopLevelTypeSynonymChanges-files", "//test-common:upgrades-SucceedsWhenATopLevelVariantAddsAVariant-files", "//test-common:upgrades-SucceedsWhenATopLevelVariantAddsAnOptionalFieldToAVariantsType-files", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-files", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedUpgradedPackage-files", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-files", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage-files", "//test-common:upgrades-SucceedsWhenAnInterfaceIsOnlyDefinedInTheInitialPackage-files", + "//test-common:upgrades-SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes-files", "//test-common:upgrades-SucceedsWhenNewFieldWithOptionalTypeIsAddedToTemplate-files", "//test-common:upgrades-SucceedsWhenNewFieldWithOptionalTypeIsAddedToTemplateChoice-files", "//test-common:upgrades-SucceedsWhenTemplateChoiceInputArgumentHasChanged-files", @@ -493,9 +503,9 @@ da_haskell_test( "//test-common:upgrades-SucceedsWhenTwoDeeplyNestedTypeSynonymsResolveToTheSameDatatypes-files", "//test-common:upgrades-SucceedsWhenUpgradingADependency-files", "//test-common:upgrades-TemplateChangedKeyType-files", + "//test-common:upgrades-TemplateChangedKeyType2-files", "//test-common:upgrades-ValidUpgrade-files", "//test-common:upgrades-WarnsWhenAnInterfaceAndATemplateAreDefinedInTheSamePackage-files", - "//test-common:upgrades-WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt-files", "//test-common:upgrades-WarnsWhenAnInterfaceIsUsedInThePackageThatItsDefinedIn-files", "//test-common:upgrades-WarnsWhenControllersOfTemplateChoiceAreChanged-files", "//test-common:upgrades-WarnsWhenObserversOfTemplateChoiceAreChanged-files", diff --git a/sdk/compiler/damlc/tests/src/DA/Test/DamlcUpgrades.hs b/sdk/compiler/damlc/tests/src/DA/Test/DamlcUpgrades.hs index 927bb2bb7589..7c7ff29fa60b 100644 --- a/sdk/compiler/damlc/tests/src/DA/Test/DamlcUpgrades.hs +++ b/sdk/compiler/damlc/tests/src/DA/Test/DamlcUpgrades.hs @@ -19,7 +19,7 @@ import SdkVersion (SdkVersioned, sdkVersion, withSdkVersions) import DA.Daml.LF.Ast.Version import Text.Regex.TDFA import qualified Data.Text as T -import Safe (fromJustNote) +import Data.Maybe (maybeToList) main :: IO () main = withSdkVersions $ do @@ -314,14 +314,28 @@ tests damlc = False setUpgradeField , test - "SucceedsWhenAnInstanceIsAddedSeparateDep" + "FailsWhenAnInstanceIsAddedSeparateDep" + (FailWithError "\ESC\\[0;91merror type checking template Main.T :\n Implementation of interface I by template T appears in this package, but does not appear in package that is being upgraded.") + versionDefault + SeparateDep + False + setUpgradeField + , test + "FailsWhenAnInstanceIsAddedUpgradedPackage" + (FailWithError "\ESC\\[0;91merror type checking template Main.T :\n Implementation of interface I by template T appears in this package, but does not appear in package that is being upgraded.") + versionDefault + DependOnV1 + True + setUpgradeField + , test + "SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep" Succeed versionDefault SeparateDep False setUpgradeField , test - "SucceedsWhenAnInstanceIsAddedUpgradedPackage" + "SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage" Succeed versionDefault DependOnV1 @@ -369,6 +383,13 @@ tests damlc = NoDependencies False setUpgradeField + , test + "TemplateChangedKeyType2" + Succeed + versionDefault + NoDependencies + False + setUpgradeField , test "RecordFieldsNewNonOptional" (FailWithError "\ESC\\[0;91merror type checking data type Main.Struct:\n The upgraded data type Struct has added new fields, but those fields are not Optional.") @@ -387,14 +408,14 @@ tests damlc = "FailsWithSynonymReturnTypeChangeInSeparatePackage" (FailWithError "\ESC\\[0;91merror type checking template Main.T choice C:\n The upgraded choice C cannot change its return type.") versionDefault - SeparateDeps + (SeparateDeps False) False setUpgradeField , test "SucceedsWhenUpgradingADependency" Succeed versionDefault - SeparateDeps + (SeparateDeps False) False setUpgradeField , test @@ -411,6 +432,45 @@ tests damlc = NoDependencies False setUpgradeField + , test + "FailsWhenDepsDowngradeVersionsWhileUsingDatatypes" + (FailWithError "\ESC\\[0;91merror type checking data type Main.Main:\n The upgraded data type Main has changed the types of some of its original fields.") + versionDefault + (SeparateDeps True) + False + setUpgradeField + , test + "SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes" + Succeed + versionDefault + (SeparateDeps True) + False + setUpgradeField + , test + "FailsWhenDependencyIsNotAValidUpgrade" + (FailWithError "\ESC\\[0;91merror while validating that dependency upgrades-example-FailsWhenDependencyIsNotAValidUpgrade-dep version 0.0.2 is a valid upgrade of version 0.0.1\n error type checking data type Dep.Dep:\n The upgraded data type Dep has added new fields, but those fields are not Optional.") + versionDefault + (SeparateDeps False) + False + setUpgradeField + , testWithAdditionalDars + "FailsWhenUpgradedFieldPackagesAreNotUpgradable" + (FailWithError "\ESC\\[0;91merror type checking data type ProjectMain.T:\n The upgraded data type T has changed the types of some of its original fields.") + versionDefault + NoDependencies + False + setUpgradeField + ["upgrades-SucceedsWhenATopLevelRecordAddsAnOptionalFieldAtTheEnd-v2.dar"] -- Note that dependencies are in different order + ["upgrades-SucceedsWhenATopLevelRecordAddsAnOptionalFieldAtTheEnd-v1.dar"] + , testWithAdditionalDars + "FailsWhenUpgradedFieldFromDifferentPackageName" + (FailWithError "\ESC\\[0;91merror type checking data type Main.A:\n The upgraded data type A has changed the types of some of its original fields.") + versionDefault + NoDependencies + False + setUpgradeField + ["upgrades-FailsWhenUpgradedFieldFromDifferentPackageName-dep-name1.dar"] + ["upgrades-FailsWhenUpgradedFieldFromDifferentPackageName-dep-name2.dar"] ] | setUpgradeField <- [True, False] ] ++ @@ -424,6 +484,8 @@ tests damlc = warnBadInterfaceInstances True doTypecheck + [] + [] , testGeneral (prefix <> "WhenAnInterfaceIsUsedInThePackageThatItsDefinedIn") "WarnsWhenAnInterfaceIsUsedInThePackageThatItsDefinedIn" @@ -433,15 +495,8 @@ tests damlc = warnBadInterfaceInstances True doTypecheck - , testGeneral - (prefix <> "WhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt") - "WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt" - (expectation "type checking template Main.T interface instance [0-9a-f]+:Main:I for Main:T:\n The template T has implemented interface I, which is defined in a previous version of this package.") - versionDefault - DependOnV1 - warnBadInterfaceInstances - True - doTypecheck + [] + [] ] | warnBadInterfaceInstances <- [True, False] , let prefix = if warnBadInterfaceInstances then "Warns" else "Fail" @@ -453,15 +508,9 @@ tests damlc = ] ) where - --contractKeysMinVersion :: LF.Version - --contractKeysMinVersion = LF.versionDefault - + -- TODO: https://github.com/digital-asset/daml/issues/19862 versionDefault :: LF.Version - versionDefault = - maxMinorVersion LF.versionDefault $ LF.versionMinor $ - fromJustNote - "Expected at least one LF 1.x version to support package upgrades." - (LF.featureMinVersion LF.featurePackageUpgrades LF.V1) + versionDefault = version1_dev test :: String @@ -472,7 +521,19 @@ tests damlc = -> Bool -> TestTree test name expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField = - testGeneral name name expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField True + testGeneral name name expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField True [] [] + + testWithAdditionalDars + :: String + -> Expectation + -> LF.Version + -> Dependency + -> Bool + -> Bool + -> [String] -> [String] + -> TestTree + testWithAdditionalDars name expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField additionalDarsV1 additionalDarsV2 = + testGeneral name name expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField True additionalDarsV1 additionalDarsV2 testGeneral :: String @@ -483,12 +544,13 @@ tests damlc = -> Bool -> Bool -> Bool + -> [String] -> [String] -> TestTree - testGeneral name location expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField doTypecheck = + testGeneral name location expectation lfVersion sharedDep warnBadInterfaceInstances setUpgradeField doTypecheck additionalDarsV1 additionalDarsV2 = let upgradeFieldTrailer = if not setUpgradeField then " (no upgrades field)" else "" doTypecheckTrailer = if not doTypecheck then " (disable typechecking)" else "" in - testCase (name <> upgradeFieldTrailer <> doTypecheckTrailer) $ + testCase (name <> upgradeFieldTrailer <> doTypecheckTrailer) $ do withTempDir $ \dir -> do let newDir = dir "newVersion" let oldDir = dir "oldVersion" @@ -496,6 +558,7 @@ tests damlc = let oldDar = oldDir "old.dar" let testRunfile path = locateRunfiles (mainWorkspace "test-common/src/main/daml/upgrades" path) + let testAdditionaDarRunfile darName = locateRunfiles (mainWorkspace "test-common" darName) v1FilePaths <- listDirectory =<< testRunfile (location "v1") let oldVersion = flip map v1FilePaths $ \path -> @@ -517,10 +580,10 @@ tests damlc = ) let sharedDir = dir "shared" let sharedDar = sharedDir "out.dar" - writeFiles sharedDir (projectFile ("upgrades-example-" <> location <> "-dep") Nothing Nothing : sharedDepFiles) + writeFiles sharedDir (projectFile "0.0.1" ("upgrades-example-" <> location <> "-dep") Nothing Nothing [] : sharedDepFiles) callProcessSilent damlc ["build", "--project-root", sharedDir, "-o", sharedDar] pure (Just sharedDar, Just sharedDar) - SeparateDeps -> do + SeparateDeps { shouldSwap } -> do depV1FilePaths <- listDirectory =<< testRunfile (location "dep-v1") let depV1Files = flip map depV1FilePaths $ \path -> ( "daml" path @@ -528,7 +591,7 @@ tests damlc = ) let depV1Dir = dir "shared-v1" let depV1Dar = depV1Dir "out.dar" - writeFiles depV1Dir (projectFile ("upgrades-example-" <> location <> "-dep-v1") Nothing Nothing : depV1Files) + writeFiles depV1Dir (projectFile "0.0.1" ("upgrades-example-" <> location <> "-dep") Nothing Nothing [] : depV1Files) callProcessSilent damlc ["build", "--project-root", depV1Dir, "-o", depV1Dar] depV2FilePaths <- listDirectory =<< testRunfile (location "dep-v2") @@ -538,19 +601,23 @@ tests damlc = ) let depV2Dir = dir "shared-v2" let depV2Dar = depV2Dir "out.dar" - writeFiles depV2Dir (projectFile ("upgrades-example-" <> location <> "-dep-v2") Nothing Nothing : depV2Files) + writeFiles depV2Dir (projectFile "0.0.2" ("upgrades-example-" <> location <> "-dep") Nothing Nothing [] : depV2Files) callProcessSilent damlc ["build", "--project-root", depV2Dir, "-o", depV2Dar] - pure (Just depV1Dar, Just depV2Dar) + if shouldSwap + then pure (Just depV2Dar, Just depV1Dar) + else pure (Just depV1Dar, Just depV2Dar) DependOnV1 -> pure (Nothing, Just oldDar) _ -> pure (Nothing, Nothing) - writeFiles oldDir (projectFile ("upgrades-example-" <> location) Nothing depV1Dar : oldVersion) + v1AdditionalDarsRunFiles <- traverse testAdditionaDarRunfile additionalDarsV1 + writeFiles oldDir (projectFile "0.0.1" ("upgrades-example-" <> location) Nothing depV1Dar v1AdditionalDarsRunFiles : oldVersion) callProcessSilent damlc ["build", "--project-root", oldDir, "-o", oldDar] - writeFiles newDir (projectFile ("upgrades-example-" <> location <> "-v2") (if setUpgradeField then Just oldDar else Nothing) depV2Dar : newVersion) + v2AdditionalDarsRunFiles <- traverse testAdditionaDarRunfile additionalDarsV2 + writeFiles newDir (projectFile "0.0.2" ("upgrades-example-" <> location) (if setUpgradeField then Just oldDar else Nothing) depV2Dar v2AdditionalDarsRunFiles : newVersion) case expectation of Succeed -> @@ -575,13 +642,13 @@ tests damlc = else when (matchTest compiledRegex stderr) $ assertFailure ("`daml build` succeeded, did not `upgrade:` field set, should NOT give a warning matching '" <> show regexWithSeverity <> "':\n" <> show stderr) where - projectFile name upgradedFile mbDep = + projectFile version name upgradedFile mbDep darDeps = ( "daml.yaml" , pure $ unlines $ [ "sdk-version: " <> sdkVersion , "name: " <> name , "source: daml" - , "version: 0.0.1" + , "version: " <> version , "dependencies:" , " - daml-prim" , " - daml-stdlib" @@ -591,9 +658,14 @@ tests damlc = ++ [" - --typecheck-upgrades=no" | not doTypecheck] ++ [" - --warn-bad-interface-instances=yes" | warnBadInterfaceInstances ] ++ ["upgrades: '" <> path <> "'" | Just path <- pure upgradedFile] - ++ ["data-dependencies:\n - '" <> path <> "'" | Just path <- pure mbDep] + ++ renderDataDeps (maybeToList mbDep ++ darDeps) ) + renderDataDeps :: [String] -> [String] + renderDataDeps [] = [] + renderDataDeps paths = + ["data-dependencies:"] ++ [" - '" <> path <> "'" | path <- paths] + writeFiles dir fs = for_ fs $ \(file, ioContent) -> do content <- ioContent @@ -610,4 +682,4 @@ data Dependency = NoDependencies | DependOnV1 | SeparateDep - | SeparateDeps + | SeparateDeps { shouldSwap :: Bool } diff --git a/sdk/daml-lf/validation/BUILD.bazel b/sdk/daml-lf/validation/BUILD.bazel index c150ce3701ac..db018d1aac5b 100644 --- a/sdk/daml-lf/validation/BUILD.bazel +++ b/sdk/daml-lf/validation/BUILD.bazel @@ -259,15 +259,29 @@ da_scala_test_suite( "//test-common:upgrades-FailsWhenAnInstanceIsDropped-v2.dar", "//test-common:upgrades-FailsWhenAnInterfaceIsDefinedInAnUpgradingPackageWhenItWasAlreadyInThePriorPackage-v1.dar", "//test-common:upgrades-FailsWhenAnInterfaceIsDefinedInAnUpgradingPackageWhenItWasAlreadyInThePriorPackage-v2.dar", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-dep.dar", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-v1.dar", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-v2.dar", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedUpgradedPackage-v1.dar", - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedUpgradedPackage-v2.dar", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedSeparateDep-dep.dar", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedSeparateDep-v1.dar", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedSeparateDep-v2.dar", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedUpgradedPackage-v1.dar", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedUpgradedPackage-v2.dar", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage-v1.dar", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage-v2.dar", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-v1.dar", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-v2.dar", "//test-common:upgrades-SucceedsWhenATopLevelVariantAddsAVariant-v1.dar", "//test-common:upgrades-SucceedsWhenATopLevelVariantAddsAVariant-v2.dar", "//test-common:upgrades-FailsWhenDatatypeChangesVariety-v1.dar", "//test-common:upgrades-FailsWhenDatatypeChangesVariety-v2.dar", + + # Test for dependency upgrades + "//test-common:upgrades-FailsWhenDepsDowngradeVersionsWhileUsingDatatypes-dep-v1.dar", + "//test-common:upgrades-FailsWhenDepsDowngradeVersionsWhileUsingDatatypes-dep-v2.dar", + "//test-common:upgrades-FailsWhenDepsDowngradeVersionsWhileUsingDatatypes-v1.dar", + "//test-common:upgrades-FailsWhenDepsDowngradeVersionsWhileUsingDatatypes-v2.dar", + "//test-common:upgrades-SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes-dep-v1.dar", + "//test-common:upgrades-SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes-dep-v2.dar", + "//test-common:upgrades-SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes-v1.dar", + "//test-common:upgrades-SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes-v2.dar", ], flaky = False, scala_deps = [ diff --git a/sdk/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Upgrading.scala b/sdk/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Upgrading.scala index 0f7f6eae21e1..83fd0b06108a 100644 --- a/sdk/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Upgrading.scala +++ b/sdk/daml-lf/validation/src/main/scala/com/digitalasset/daml/lf/validation/Upgrading.scala @@ -196,6 +196,11 @@ object UpgradeError { override def message: String = s"Implementation of interface $iface by template $tpl appears in package that is being upgraded, but does not appear in this package." } + + final case class ForbiddenNewInstance(tpl: Ref.DottedName, iface: Ref.TypeConName) extends Error { + override def message: String = + s"Implementation of interface $iface by template $tpl appears in this package, but does not appear in package that is being upgraded." + } } sealed abstract class UpgradedRecordOrigin @@ -604,7 +609,7 @@ case class TypecheckUpgrades( val moduleWithMetadata = module.map(ModuleWithMetadata) for { - (existingTemplates, _new) <- checkDeleted( + (existingTemplates, newTemplates) <- checkDeleted( module.map(_.templates), (name: Ref.DottedName, _: Ast.Template) => UpgradeError.MissingTemplate(name), ) @@ -613,15 +618,13 @@ case class TypecheckUpgrades( (_ifaceDel, ifaceExisting, _ifaceNew) = extractDelExistNew(ifaceDts) _ <- checkContinuedIfaces(ifaceExisting) - (_instanceExisting, _instanceNew) <- - checkDeleted( - module.map(flattenInstances(_)), - (tplImpl: (Ref.DottedName, Ref.TypeConName), _: (Ast.Template, Ast.TemplateImplements)) => - { - val (tpl, impl) = tplImpl - UpgradeError.MissingImplementation(tpl, impl) - }, - ) + (instanceDel, _instanceExisting, instanceNew) = extractDelExistNew( + module.map(flattenInstances) + ) + _ <- checkDeletedInstances(instanceDel) + _ <- checkAddedInstances(instanceNew.view.filterKeys { case (tyCon, _) => + !newTemplates.contains(tyCon) + }.toMap) (existingDatatypes, _new) <- checkDeleted( unownedDts, @@ -643,6 +646,22 @@ case class TypecheckUpgrades( ).map(_ => ()) } + private def checkDeletedInstances( + deletedInstances: Map[(Ref.DottedName, TypeConName), (Ast.Template, Ast.TemplateImplements)] + ): Try[Unit] = + deletedInstances.headOption match { + case Some(((tpl, iface), _)) => fail(UpgradeError.MissingImplementation(tpl, iface)) + case None => Success(()) + } + + private def checkAddedInstances( + newInstances: Map[(Ref.DottedName, TypeConName), (Ast.Template, Ast.TemplateImplements)] + ): Try[Unit] = + newInstances.headOption match { + case Some(((tpl, iface), _)) => fail(UpgradeError.ForbiddenNewInstance(tpl, iface)) + case None => Success(()) + } + private def checkTemplate( templateAndName: (Ref.DottedName, Upgrading[Ast.Template]) ): Try[Unit] = { diff --git a/sdk/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/upgrade/UpgradesSpecBase.scala b/sdk/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/upgrade/UpgradesSpecBase.scala index a442736ec9f0..405e131fd92a 100644 --- a/sdk/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/upgrade/UpgradesSpecBase.scala +++ b/sdk/daml-lf/validation/src/test/scala/com/digitalasset/daml/lf/validation/upgrade/UpgradesSpecBase.scala @@ -719,23 +719,47 @@ trait LongTests { this: UpgradesSpec => } yield result } - "Succeeds when an instance is added (separate dep)." in { + "Fails when an instance is added (separate dep)." in { for { - _ <- uploadPackage("test-common/upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-dep.dar") + _ <- uploadPackage("test-common/upgrades-FailsWhenAnInstanceIsAddedSeparateDep-dep.dar") result <- testPackagePair( - "test-common/upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-v1.dar", - "test-common/upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-v2.dar", + "test-common/upgrades-FailsWhenAnInstanceIsAddedSeparateDep-v1.dar", + "test-common/upgrades-FailsWhenAnInstanceIsAddedSeparateDep-v2.dar", assertPackageUpgradeCheck( - None + Some( + "Implementation of interface .*:Dep:I by template T appears in this package, but does not appear in package that is being upgraded." + ) ), ) } yield result } - "Succeeds when an instance is added (upgraded package)." in { + "Fails when an instance is added (upgraded package)." in { + testPackagePair( + "test-common/upgrades-FailsWhenAnInstanceIsAddedUpgradedPackage-v1.dar", + "test-common/upgrades-FailsWhenAnInstanceIsAddedUpgradedPackage-v2.dar", + assertPackageUpgradeCheck( + Some( + "Implementation of interface .*:Main:I by template T appears in this package, but does not appear in package that is being upgraded." + ) + ), + ) + } + + "Succeeds when an instance is added to a new template (upgraded package)." in { + testPackagePair( + "test-common/upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage-v1.dar", + "test-common/upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage-v2.dar", + assertPackageUpgradeCheck( + None + ), + ) + } + + "Succeeds when an instance is added to a new template (separate dep)." in { testPackagePair( - "test-common/upgrades-SucceedsWhenAnInstanceIsAddedUpgradedPackage-v1.dar", - "test-common/upgrades-SucceedsWhenAnInstanceIsAddedUpgradedPackage-v2.dar", + "test-common/upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-v1.dar", + "test-common/upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-v2.dar", assertPackageUpgradeCheck( None ), diff --git a/sdk/language-support/ts/codegen/src/TsCodeGenMain.hs b/sdk/language-support/ts/codegen/src/TsCodeGenMain.hs index 8009ca46d3ae..1d952f0e94cd 100644 --- a/sdk/language-support/ts/codegen/src/TsCodeGenMain.hs +++ b/sdk/language-support/ts/codegen/src/TsCodeGenMain.hs @@ -108,7 +108,7 @@ mergePackageMap ps = fst <$> foldM merge mempty ps Either T.Text (Map.Map PackageId (PackageReference, Package), Set.Set (PackageName, PackageVersion)) merge (pkgs, usedUnitIds) (pkgId, pkg, isMain) = do let pkgIsUtil = isUtilityPackage pkg - supportsUpgrades = not pkgIsUtil && packageLfVersion pkg `supports` featurePackageUpgrades + supportsUpgrades = pkgSupportsUpgrades pkg pkgRef = case packageMetadata pkg of Just (PackageMetadata {..}) | supportsUpgrades -> @@ -153,14 +153,6 @@ main = do T.putStrLn $ "Generating " <> pkgDesc daml2js Daml2jsParams{..} -isUtilityPackage :: Package -> Bool -isUtilityPackage pkg = - all (\mod -> - null (moduleTemplates mod) - && null (moduleInterfaces mod) - && not (any (getIsSerializable . dataSerializable) $ moduleDataTypes mod) - ) $ packageModules pkg - data PackageReference = PkgNameVer (PackageName, PackageVersion) | PkgId PackageId diff --git a/sdk/test-common/BUILD.bazel b/sdk/test-common/BUILD.bazel index 710296f835b6..e728ad3f04a8 100644 --- a/sdk/test-common/BUILD.bazel +++ b/sdk/test-common/BUILD.bazel @@ -198,6 +198,7 @@ da_scala_dar_resources_library( # More more more tests ported from DamlcUpgrades.hs "FailsWithSynonymReturnTypeChangeInSeparatePackage", "SucceedsWhenUpgradingADependency", + "FailsWhenDependencyIsNotAValidUpgrade", ] ] @@ -217,7 +218,8 @@ da_scala_dar_resources_library( for identifier in [ # More more more tests ported from DamlcUpgrades.hs "FailsWhenAnInstanceIsDropped", - "SucceedsWhenAnInstanceIsAddedSeparateDep", + "FailsWhenAnInstanceIsAddedSeparateDep", + "SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep", ] ] @@ -242,6 +244,70 @@ da_scala_dar_resources_library( ] ] +[ + [ + filegroup( + name = "upgrades-{}-files".format(identifier), + srcs = glob(["src/main/daml/upgrades/{}/*/*.daml".format(identifier)]), + visibility = ["//visibility:public"], + ), + daml_compile( + name = "upgrades-{}-dep-v1".format(identifier), + srcs = glob(["src/main/daml/upgrades/{}/dep-v1/*.daml".format(identifier)]), + dependencies = ["//daml-script/daml:daml-script-1.dev.dar"], + ghc_options = default_damlc_opts + ["--ghc-option=-Wno-unused-imports"], + project_name = "upgrades-example-{}-dep".format(identifier), + target = "1.dev", + version = "1.0.0", + visibility = ["//visibility:public"], + ), + daml_compile( + name = "upgrades-{}-dep-v2".format(identifier), + srcs = glob(["src/main/daml/upgrades/{}/dep-v2/*.daml".format(identifier)]), + dependencies = ["//daml-script/daml:daml-script-1.dev.dar"], + ghc_options = default_damlc_opts + ["--ghc-option=-Wno-unused-imports"], + project_name = "upgrades-example-{}-dep".format(identifier), + target = "1.dev", + # We want to check the validity of this upgrade on the ledger + # client, not during compilation + typecheck_upgrades = False, + upgrades = "//test-common:upgrades-{}-dep-v1.dar".format(identifier), + version = "2.0.0", + visibility = ["//visibility:public"], + ), + daml_compile( + name = "upgrades-{}-v1".format(identifier), + srcs = glob(["src/main/daml/upgrades/{}/v1/*.daml".format(identifier)]), + data_dependencies = ["//test-common:upgrades-{}-dep-v2.dar".format(identifier)], + dependencies = ["//daml-script/daml:daml-script-1.dev.dar"], + ghc_options = default_damlc_opts + ["--ghc-option=-Wno-unused-imports"], + project_name = "upgrades-example-{}".format(identifier), + target = "1.dev", + version = "1.0.0", + visibility = ["//visibility:public"], + ), + daml_compile( + name = "upgrades-{}-v2".format(identifier), + srcs = glob(["src/main/daml/upgrades/{}/v2/*.daml".format(identifier)]), + data_dependencies = ["//test-common:upgrades-{}-dep-v1.dar".format(identifier)], + dependencies = ["//daml-script/daml:daml-script-1.dev.dar"], + ghc_options = default_damlc_opts + ["--ghc-option=-Wno-unused-imports"], + project_name = "upgrades-example-{}".format(identifier), + target = "1.dev", + # We want to check the validity of this upgrade on the ledger + # client, not during compilation + typecheck_upgrades = False, + upgrades = "//test-common:upgrades-{}-v1.dar".format(identifier), + version = "2.0.0", + visibility = ["//visibility:public"], + ), + ] + for identifier in [ + "FailsWhenDepsDowngradeVersionsWhileUsingDatatypes", + "SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes", + ] +] + [ [ filegroup( @@ -297,6 +363,7 @@ da_scala_dar_resources_library( ("MissingChoice", {}, {}), ("RecordFieldsNewNonOptional", {}, {}), ("TemplateChangedKeyType", {}, {}), + ("TemplateChangedKeyType2", {}, {}), ("ValidUpgrade", {}, {}), ("ValidParameterizedTypesUpgrade", {}, {}), ("ValidKeyTypeEquality", {}, {}), @@ -376,28 +443,33 @@ da_scala_dar_resources_library( ("FailsWhenAnInterfaceIsDefinedInAnUpgradingPackageWhenItWasAlreadyInThePriorPackage", {}, {}), ("SucceedsWhenAnInterfaceIsOnlyDefinedInTheInitialPackage", {}, {}), ( - "SucceedsWhenAnInstanceIsAddedUpgradedPackage", + "FailsWhenAnInstanceIsAddedUpgradedPackage", {}, {"data_dependencies": [ - "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedUpgradedPackage-v1.dar", + "//test-common:upgrades-FailsWhenAnInstanceIsAddedUpgradedPackage-v1.dar", ]}, ), ( - "WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt", + "SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage", {}, {"data_dependencies": [ - "//test-common:upgrades-WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt-v1.dar", + "//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage-v1.dar", ]}, ), + ( + "SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep", + {"data_dependencies": ["//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-dep.dar"]}, + {"data_dependencies": ["//test-common:upgrades-SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep-dep.dar"]}, + ), ( "FailsWhenAnInstanceIsDropped", {"data_dependencies": ["//test-common:upgrades-FailsWhenAnInstanceIsDropped-dep.dar"]}, {"data_dependencies": ["//test-common:upgrades-FailsWhenAnInstanceIsDropped-dep.dar"]}, ), ( - "SucceedsWhenAnInstanceIsAddedSeparateDep", - {"data_dependencies": ["//test-common:upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-dep.dar"]}, - {"data_dependencies": ["//test-common:upgrades-SucceedsWhenAnInstanceIsAddedSeparateDep-dep.dar"]}, + "FailsWhenAnInstanceIsAddedSeparateDep", + {"data_dependencies": ["//test-common:upgrades-FailsWhenAnInstanceIsAddedSeparateDep-dep.dar"]}, + {"data_dependencies": ["//test-common:upgrades-FailsWhenAnInstanceIsAddedSeparateDep-dep.dar"]}, ), ("FailsOnlyInModuleNotInReexports", {}, {}), ("FailsWithSynonymReturnTypeChange", {}, {}), diff --git a/sdk/test-common/BUILD.bazel.rej b/sdk/test-common/BUILD.bazel.rej new file mode 100644 index 000000000000..a5a5195f9b09 --- /dev/null +++ b/sdk/test-common/BUILD.bazel.rej @@ -0,0 +1,9 @@ +diff a/sdk/test-common/BUILD.bazel b/sdk/test-common/BUILD.bazel (rejected hunks) +@@ -108,6 +108,7 @@ da_scala_dar_resources_library( + "MissingChoice", + "RecordFieldsNewNonOptional", + "TemplateChangedKeyType", ++ "TemplateChangedKeyType2", + "ValidUpgrade", + + # Ported from DamlcUpgrades.hs diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedSeparateDep/dep/Dep.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedSeparateDep/dep/Dep.daml similarity index 100% rename from sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedSeparateDep/dep/Dep.daml rename to sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedSeparateDep/dep/Dep.daml diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedSeparateDep/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedSeparateDep/v1/Main.daml similarity index 100% rename from sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedSeparateDep/v1/Main.daml rename to sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedSeparateDep/v1/Main.daml diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedSeparateDep/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedSeparateDep/v2/Main.daml similarity index 100% rename from sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedSeparateDep/v2/Main.daml rename to sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedSeparateDep/v2/Main.daml diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedUpgradedPackage/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedUpgradedPackage/v1/Main.daml similarity index 100% rename from sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedUpgradedPackage/v1/Main.daml rename to sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedUpgradedPackage/v1/Main.daml diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedUpgradedPackage/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedUpgradedPackage/v2/Main.daml similarity index 78% rename from sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedUpgradedPackage/v2/Main.daml rename to sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedUpgradedPackage/v2/Main.daml index 64b9290843a6..19deb58e7247 100644 --- a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedUpgradedPackage/v2/Main.daml +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenAnInstanceIsAddedUpgradedPackage/v2/Main.daml @@ -2,7 +2,7 @@ -- SPDX-License-Identifier: Apache-2.0 module Main where -import qualified "upgrades-example-SucceedsWhenAnInstanceIsAddedUpgradedPackage" Main as V1 +import qualified "upgrades-example-FailsWhenAnInstanceIsAddedUpgradedPackage" Main as V1 template T with p: Party where diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/dep-v1/Dep.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/dep-v1/Dep.daml new file mode 100644 index 000000000000..15ece72a64dd --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/dep-v1/Dep.daml @@ -0,0 +1,6 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Dep where + +data Dep = Dep { dep : Text } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/dep-v2/Dep.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/dep-v2/Dep.daml new file mode 100644 index 000000000000..3298bebe3538 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/dep-v2/Dep.daml @@ -0,0 +1,6 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Dep where + +data Dep = Dep { dep : Text, nonOptionalField : Text } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/v1/Main.daml new file mode 100644 index 000000000000..3ce6ce2337e3 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/v1/Main.daml @@ -0,0 +1,8 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +import Dep + +data Main = Main { field : Dep } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/v2/Main.daml new file mode 100644 index 000000000000..012a084a030e --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDependencyIsNotAValidUpgrade/v2/Main.daml @@ -0,0 +1,8 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +import Dep + +data Main = Main { field : Dep, optionalField : Optional Text } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/dep-v1/Dep.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/dep-v1/Dep.daml new file mode 100644 index 000000000000..15ece72a64dd --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/dep-v1/Dep.daml @@ -0,0 +1,6 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Dep where + +data Dep = Dep { dep : Text } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/dep-v2/Dep.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/dep-v2/Dep.daml new file mode 100644 index 000000000000..15ece72a64dd --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/dep-v2/Dep.daml @@ -0,0 +1,6 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Dep where + +data Dep = Dep { dep : Text } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/v1/Main.daml new file mode 100644 index 000000000000..3ce6ce2337e3 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/v1/Main.daml @@ -0,0 +1,8 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +import Dep + +data Main = Main { field : Dep } diff --git a/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/v2/Main.daml new file mode 100644 index 000000000000..3ce6ce2337e3 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/FailsWhenDepsDowngradeVersionsWhileUsingDatatypes/v2/Main.daml @@ -0,0 +1,8 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +import Dep + +data Main = Main { field : Dep } diff --git a/sdk/test-common/src/main/daml/upgrades/WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/dep/Dep.daml similarity index 92% rename from sdk/test-common/src/main/daml/upgrades/WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt/v1/Main.daml rename to sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/dep/Dep.daml index 2a757470c530..2c284d1f67e9 100644 --- a/sdk/test-common/src/main/daml/upgrades/WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt/v1/Main.daml +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/dep/Dep.daml @@ -1,7 +1,7 @@ -- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -module Main where +module Dep where data IView = IView { i : Text } interface I where viewtype IView diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/v1/Main.daml new file mode 100644 index 000000000000..fe1679ab0670 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/v1/Main.daml @@ -0,0 +1,7 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +-- We need a dummy datatype for the package to have a name +data Dummy = Dummy {} \ No newline at end of file diff --git a/sdk/test-common/src/main/daml/upgrades/WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/v2/Main.daml similarity index 52% rename from sdk/test-common/src/main/daml/upgrades/WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt/v2/Main.daml rename to sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/v2/Main.daml index 1cdbcf1688e8..7bfad20cc2e6 100644 --- a/sdk/test-common/src/main/daml/upgrades/WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt/v2/Main.daml +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateSeparateDep/v2/Main.daml @@ -2,13 +2,13 @@ -- SPDX-License-Identifier: Apache-2.0 module Main where -import qualified "upgrades-example-WarnsWhenAnInterfaceIsDefinedAndThenUsedInAPackageThatUpgradesIt" Main as V1 -data IView = IView { i : Text } +import Dep template T with p: Party where signatory p - interface instance V1.I for T where - view = V1.IView "hi" + interface instance I for T where + view = IView "hi" method1 = 2 +data Dummy = Dummy {} \ No newline at end of file diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage/v1/Main.daml new file mode 100644 index 000000000000..fe1679ab0670 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage/v1/Main.daml @@ -0,0 +1,7 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +-- We need a dummy datatype for the package to have a name +data Dummy = Dummy {} \ No newline at end of file diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage/v2/Main.daml new file mode 100644 index 000000000000..5315b04884cf --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage/v2/Main.daml @@ -0,0 +1,21 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where +import qualified "upgrades-example-SucceedsWhenAnInstanceIsAddedToNewTemplateUpgradedPackage" Main as V1 + +data Dummy = Dummy {} + +template T with + p: Party + where + signatory p + interface instance I for T where + view = IView "hi" + method1 = 1 + +data IView = IView { i : Text } + +interface I where + viewtype IView + method1 : Int diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/dep-v1/Dep.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/dep-v1/Dep.daml new file mode 100644 index 000000000000..9382be743341 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/dep-v1/Dep.daml @@ -0,0 +1,7 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Dep where + +dep : Text +dep = "dep-v1" diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/dep-v2/Dep.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/dep-v2/Dep.daml new file mode 100644 index 000000000000..b6d6c93bb972 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/dep-v2/Dep.daml @@ -0,0 +1,7 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Dep where + +dep : Text +dep = "dep-v2" diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/v1/Main.daml new file mode 100644 index 000000000000..96b8316b9aea --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/v1/Main.daml @@ -0,0 +1,9 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +import Dep + +self : Text +self = "v1 with " <> dep diff --git a/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/v2/Main.daml new file mode 100644 index 000000000000..70694c64e8dd --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/SucceedsWhenDepsDowngradeVersionsWithoutUsingDatatypes/v2/Main.daml @@ -0,0 +1,9 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +import Dep + +self : Text +self = "v2 with " <> dep diff --git a/sdk/test-common/src/main/daml/upgrades/TemplateChangedKeyType2/v1/Main.daml b/sdk/test-common/src/main/daml/upgrades/TemplateChangedKeyType2/v1/Main.daml new file mode 100644 index 000000000000..e03221710474 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/TemplateChangedKeyType2/v1/Main.daml @@ -0,0 +1,15 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +data TKey + = TKey1 with p : Party + | TKey2 with p : Party + +template T with + p: Party + where + signatory p + key (TKey1 p) : TKey + maintainer key.p diff --git a/sdk/test-common/src/main/daml/upgrades/TemplateChangedKeyType2/v2/Main.daml b/sdk/test-common/src/main/daml/upgrades/TemplateChangedKeyType2/v2/Main.daml new file mode 100644 index 000000000000..5d6b8d68fad3 --- /dev/null +++ b/sdk/test-common/src/main/daml/upgrades/TemplateChangedKeyType2/v2/Main.daml @@ -0,0 +1,16 @@ +-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. +-- SPDX-License-Identifier: Apache-2.0 + +module Main where + +data TKey + = TKey1 with p : Party + | TKey2 with p : Party + | TKey3 with p : Party + +template T with + p: Party + where + signatory p + key (TKey1 p) : TKey + maintainer key.p