Skip to content

Commit 9e9dcac

Browse files
Data dependency exports fix backport (#20601)
* Fix exports for data dependencies (#20354) * Fix exports for data dependencies * Update platform independence * Fix generated exports * Fix reexport only data deps, rename exports def * Fix various issues with exports * Change to be forward backwards compatible when building old LF versions * Update dar hashes * Use old style exports for stdlib + prim * Small cleanup, add test * Add `--ignore-data-deps-visibility` flag * Fix DamlScriptTestRunner following #20337 * Address comments from #20354 * Update from comments
1 parent bd51687 commit 9e9dcac

File tree

11 files changed

+245
-70
lines changed

11 files changed

+245
-70
lines changed

Diff for: sdk/compiler/daml-lf-ast/src/DA/Daml/LF/Ast/Util.hs

+15
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,21 @@ partitionDefinitions = foldr f ([], [], [], [], [], [])
325325
DException e -> over _5 (e:)
326326
DInterface i -> over _6 (i:)
327327

328+
-- | All names of top level exportable definitions (does not include data constructors/record accessors, as they are covered by the type name)
329+
topLevelExportables :: [Definition] -> [T.Text]
330+
topLevelExportables defs =
331+
let (syns, dataTypes, values, templates, exceptions, interfaces) = partitionDefinitions defs
332+
in mconcat
333+
[ last . unTypeSynName . synName <$> syns
334+
, last . unTypeConName . dataTypeCon <$> dataTypes
335+
, unExprValName . fst . dvalBinder <$> values
336+
, last . unTypeConName . tplTypeCon <$> templates -- Template names
337+
, mconcat $ fmap (unChoiceName . chcName) . NM.elems . tplChoices <$> templates -- Template Choice names
338+
, last . unTypeConName . exnName <$> exceptions
339+
, last . unTypeConName . intName <$> interfaces -- Interface names
340+
, mconcat $ fmap (unChoiceName . chcName) . NM.elems . intChoices <$> interfaces -- Interface Choice names
341+
]
342+
328343
-- | This is the analogue of GHC’s moduleNameString for the LF
329344
-- `ModuleName` type.
330345
moduleNameString :: ModuleName -> T.Text

Diff for: sdk/compiler/damlc/daml-compiler/src/DA/Daml/Compiler/DataDependencies.hs

+54-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module DA.Daml.Compiler.DataDependencies
1111
) where
1212

1313
import DA.Pretty hiding (first)
14+
import Control.Applicative
1415
import Control.Monad
1516
import Control.Monad.State.Strict
1617
import Data.Bifunctor (first)
@@ -85,6 +86,8 @@ data Config = Config
8586
-- ^ Information about dependencies (not data-dependencies)
8687
, configSdkPrefix :: [T.Text]
8788
-- ^ prefix to use for current SDK in data-dependencies
89+
, configIgnoreExplicitExports :: Bool
90+
-- ^ Should explicit export information be disregarded, and all definitions in this module be exported
8891
}
8992

9093
data Env = Env
@@ -324,9 +327,32 @@ generateSrcFromLf env = noLoc mod
324327
, modRefImpSpec = EmptyImpSpec
325328
}
326329

330+
{- # Exports/Re-exports #
331+
Before Daml 3.3, data dependencies only preserved re-export information, that is explicit definitions that the
332+
data dependency re-exports from another module. For definitions defined in this module, we exported everything
333+
(using `module ThisModule` in the export list)
334+
335+
These re-exports were/still are defined as top level metadata stubs, of the name `$$export<n>` with one definition
336+
per re-export (with <n> counting up from 0). Note that a single re-export included information about a types fields and constructors
337+
338+
This information is encoded at the type level using a TStruct, the value of these definitions is error.
339+
Post 2.10, we have added explicit information about local exports, under definitions of `$$explicitExport<n>`. We did not
340+
rename the existing re-exports name of `$$export<n>` for backwards compatibility, even though it isn't correct.
341+
342+
The following packages however will not have these new explicit exports:
343+
- Stable packages, for these are generated directly, and do not use LFConversion
344+
- Stdlib + prim, for these packages have special behaviour with replaced definitions that lead to convoluted exports
345+
- Packages compiled before this feature was implemented, i.e. pre 2.10
346+
In order to differentiate between these packages, and packages with modules that have no explicit exports (i.e. only typeclasses instances or only re-exports),
347+
we include a tag `$$explicitExports` of type `()`.
348+
When this tag is not present, we revert to the usual `module ThisModule` exports.
349+
350+
Note that since fixing this behaviour is a breaking change, there is a second scenario in which we ignore these tags and use `module ThisModule` regardless
351+
This is when the `--ignore-data-dep-visibility` flag is passed, for backwards compatibility.
352+
-}
327353
genExports :: Gen [LIE GhcPs]
328354
genExports = (++)
329-
<$> (sequence $ selfReexport : classReexports)
355+
<$> (sequence $ selfReexport <> classReexports)
330356
<*> allExports
331357

332358
genDecls :: Gen [LHsDecl GhcPs]
@@ -352,20 +378,35 @@ generateSrcFromLf env = noLoc mod
352378
, Just methodName <- [getClassMethodName fieldName]
353379
]
354380

381+
usesExplicitExports :: Bool
382+
usesExplicitExports = not $ null $ do
383+
guard $ not $ configIgnoreExplicitExports $ envConfig env
384+
LF.DefValue {dvalBinder=(name, _)} <- NM.toList . LF.moduleValues $ envMod env
385+
guard $ name == LFC.explicitExportsTag
386+
387+
qualNameToSynName :: LFC.QualName -> LF.TypeSynName
388+
qualNameToSynName (LFC.QualName (LF.Qualified {LF.qualObject})) = LF.TypeSynName $ T.split (=='.') $ T.pack $ occNameString qualObject
389+
355390
allExports :: Gen [LIE GhcPs]
356391
allExports = sequence $ do
357392
LF.DefValue {dvalBinder=(name, ty)} <- NM.toList . LF.moduleValues $ envMod env
358-
Just _ <- [LFC.unExportName name] -- We don't really care about the order of exports
393+
-- We don't really care about the order of exports
394+
-- Export both re-exports and explicit exports with the same mechanism
395+
Just _ <- [LFC.unReExportName name <|> (guard usesExplicitExports >> LFC.unExportName name)]
359396
Just export <- [LFC.decodeExportInfo ty]
360-
pure $ mkLIE export
397+
mkLIE export
361398
where
362-
mkLIE :: LFC.ExportInfo -> Gen (LIE GhcPs)
363-
mkLIE = fmap noLoc . \case
399+
mkLIE :: LFC.ExportInfo -> [Gen (LIE GhcPs)]
400+
mkLIE = fmap (fmap noLoc) . \case
364401
LFC.ExportInfoVal name ->
365-
IEVar NoExt
402+
pure $ IEVar NoExt
366403
<$> mkWrappedRdrName IEName name
404+
-- Classes that are duplicated in non-data-dependencies are replaced with re-exports of the other class
405+
-- (for compatibility with old stdlibs)
406+
-- As such, we do not want to export definitions that have already been re-exported/replaced
407+
LFC.ExportInfoTC name _ _ | qualNameToSynName name `MS.member` classReexportMap -> []
367408
LFC.ExportInfoTC name pieces fields ->
368-
IEThingWith NoExt
409+
pure $ IEThingWith NoExt
369410
<$> mkWrappedRdrName IEType name
370411
<*> pure NoIEWildcard
371412
<*> mapM (mkWrappedRdrName IEName) pieces
@@ -385,9 +426,12 @@ generateSrcFromLf env = noLoc mod
385426
mkFieldLblRdrName :: FieldLbl LFC.QualName -> Gen (Located (FieldLbl RdrName))
386427
mkFieldLblRdrName = fmap noLoc . traverse mkRdrName
387428

388-
selfReexport :: Gen (LIE GhcPs)
389-
selfReexport = pure . noLoc $
390-
IEModuleContents noExt (noLoc ghcModName)
429+
-- We only reexport self (i.e. module Self) when not using explicit exports
430+
selfReexport :: [Gen (LIE GhcPs)]
431+
selfReexport =
432+
[ pure . noLoc $ IEModuleContents noExt (noLoc ghcModName)
433+
| not usesExplicitExports
434+
]
391435

392436
classReexports :: [Gen (LIE GhcPs)]
393437
classReexports = map snd3 (MS.elems classReexportMap)

Diff for: sdk/compiler/damlc/daml-ghc-util/src/DA/Daml/UtilGHC.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pattern GHC_Types <- ModuleIn DamlPrim "GHC.Types"
9797
pattern GHC_Show <- ModuleIn DamlPrim "GHC.Show"
9898

9999
-- daml-stdlib module patterns
100-
pattern DA_Action, DA_Internal_LF, DA_Internal_Prelude, DA_Internal_Record, DA_Internal_Desugar, DA_Internal_Template_Functions, DA_Internal_Exception, DA_Internal_Interface, DA_Internal_Template :: GHC.Module
100+
pattern DA_Action, DA_Internal_LF, DA_Internal_Prelude, DA_Internal_Record, DA_Internal_Desugar, DA_Internal_Template_Functions, DA_Internal_Exception, DA_Internal_Interface, DA_Internal_Template, DA_Internal_Compatible :: GHC.Module
101101
pattern DA_Action <- ModuleIn DamlStdlib "DA.Action"
102102
pattern DA_Internal_LF <- ModuleIn DamlStdlib "DA.Internal.LF"
103103
pattern DA_Internal_Prelude <- ModuleIn DamlStdlib "DA.Internal.Prelude"
@@ -107,6 +107,7 @@ pattern DA_Internal_Template_Functions <- ModuleIn DamlStdlib "DA.Internal.Templ
107107
pattern DA_Internal_Exception <- ModuleIn DamlStdlib "DA.Internal.Exception"
108108
pattern DA_Internal_Interface <- ModuleIn DamlStdlib "DA.Internal.Interface"
109109
pattern DA_Internal_Template <- ModuleIn DamlStdlib "DA.Internal.Template"
110+
pattern DA_Internal_Compatible <- ModuleIn DamlStdlib "DA.Internal.Compatible"
110111

111112
-- | Deconstruct a dictionary function (DFun) identifier into a tuple
112113
-- containing, in order:

Diff for: sdk/compiler/damlc/daml-lf-conversion/src/DA/Daml/LFConversion.hs

+53-21
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,6 @@ convertModuleContents env mc = do
761761
templates <- convertTemplateDefs env mc
762762
exceptions <- convertExceptionDefs env mc
763763
interfaces <- convertInterfaces env mc
764-
exports <- convertExports env mc
765764
let fixities = convertFixities mc
766765
defs =
767766
types
@@ -770,9 +769,10 @@ convertModuleContents env mc = do
770769
++ definitions
771770
++ interfaces
772771
++ depOrphanModules
773-
++ exports
774772
++ fixities
775-
pure defs
773+
-- Exports need to know what is defined to know if it should export it
774+
exports <- convertExports env mc defs
775+
pure $ defs ++ exports
776776

777777
data Consuming = PreConsuming
778778
| Consuming
@@ -961,22 +961,49 @@ convertFixities = zipWith mkFixityDef [0..] . mcFixities
961961
(fixityName i)
962962
(encodeFixityInfo fixityInfo)
963963

964-
convertExports :: Env -> ModuleContents -> ConvertM [Definition]
965-
convertExports env mc = do
966-
let externalExportInfos = filter isExternalAvailInfo (mcExports mc)
967-
exportInfos <- mapM availInfoToExportInfo externalExportInfos
968-
pure $ zipWith mkExportDef [0..] exportInfos
964+
convertExports :: SdkVersioned => Env -> ModuleContents -> [Definition] -> ConvertM [Definition]
965+
convertExports env mc existingDefs = do
966+
let externalReExportInfos = filter (isReExportName . GHC.availName) (mcExports mc)
967+
reExportInfos <- mapM availInfoToExportInfo externalReExportInfos
968+
let reExportDefs = zipWith mkExportDef (reExportName <$> [0..]) reExportInfos
969+
970+
-- [SW] Use old style exports for prim/stdlib, i.e. export everything
971+
-- I tried to have these also export normally, but ran into issues with proto3-suite overflowing heap when parsing stdlib
972+
-- (when using `compile` directly, without daml-assistant)
973+
if isPrimOrStdlib
974+
then pure reExportDefs
975+
else do
976+
let externalExportInfos = filter (isExportName . GHC.availName) (mcExports mc) \\ externalReExportInfos
977+
exportInfos <- mapM availInfoToExportInfo externalExportInfos
978+
let exportDefs = zipWith mkExportDef (exportName <$> [0..]) exportInfos
979+
pure $ explicitExportsDef : reExportDefs <> exportDefs
969980
where
970-
isExternalAvailInfo :: GHC.AvailInfo -> Bool
971-
isExternalAvailInfo = isExternalName . GHC.availName
972-
where
973-
isExternalName name =
974-
not $
975-
nameIsLocalOrFrom thisModule name
976-
|| isSystemName name
977-
|| isWiredInName name
978-
|| maybe False (isInternal . GHC.moduleName) (nameModule_maybe name)
979-
thisModule = GHC.Module (envModuleUnitId env) (envGHCModuleName env)
981+
isReExportName :: Name -> Bool
982+
isReExportName name = not $
983+
isSystemName name
984+
|| isWiredInName name
985+
|| nameIsLocalOrFrom thisModule name
986+
|| maybe False (isInternal . GHC.moduleName) (nameModule_maybe name)
987+
988+
isPrimOrStdlib :: Bool
989+
isPrimOrStdlib = envModuleUnitId env == damlStdlib || envModuleUnitId env == stringToUnitId "daml-prim"
990+
991+
thisModule :: GHC.Module
992+
thisModule = GHC.Module (envModuleUnitId env) (envGHCModuleName env)
993+
994+
localExportables :: [T.Text]
995+
localExportables = topLevelExportables existingDefs
996+
997+
-- If name is local but isn't defined in other generated definitions, don't export it
998+
isLocallyUndefined :: Name -> Bool
999+
isLocallyUndefined name | nameIsLocalOrFrom thisModule name = not $ getOccText name `elem` localExportables
1000+
isLocallyUndefined _ = False
1001+
1002+
isExportName :: Name -> Bool
1003+
isExportName name = not $
1004+
isSystemName name
1005+
|| isWiredInName name
1006+
|| isLocallyUndefined name
9801007

9811008
availInfoToExportInfo :: GHC.AvailInfo -> ConvertM ExportInfo
9821009
availInfoToExportInfo = \case
@@ -1004,10 +1031,15 @@ convertExports env mc = do
10041031
flSelector <- convertQualName (flSelector f)
10051032
pure f { flSelector }
10061033

1007-
mkExportDef :: Integer -> ExportInfo -> Definition
1008-
mkExportDef i info =
1034+
mkExportDef :: LF.ExprValName -> ExportInfo -> Definition
1035+
mkExportDef name info =
10091036
let exportType = encodeExportInfo info
1010-
in DValue (mkMetadataStub (exportName i) exportType)
1037+
in DValue (mkMetadataStub name exportType)
1038+
1039+
-- Tag for explicit exports, so we can differentiate between a module no exports and a module compiled either before
1040+
-- explicit exports was implemented, or is stdlib/prim
1041+
explicitExportsDef :: Definition
1042+
explicitExportsDef = DValue (mkMetadataStub explicitExportsTag (LF.TBuiltin LF.BTUnit))
10111043

10121044
defNewtypeWorker :: NamedThing a => Env -> a -> TypeConName -> DataCon
10131045
-> [(TypeVarName, LF.Kind)] -> [(FieldName, LF.Type)] -> Definition

Diff for: sdk/compiler/damlc/daml-lf-conversion/src/DA/Daml/LFConversion/MetadataEncoding.hs

+19-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ module DA.Daml.LFConversion.MetadataEncoding
2727
, encodeModuleImports
2828
, decodeModuleImports
2929
-- * Exports
30+
, reExportName
31+
, unReExportName
3032
, exportName
3133
, unExportName
34+
, explicitExportsTag
3235
, ExportInfo (..)
3336
, QualName (..)
3437
, encodeExportInfo
@@ -233,14 +236,28 @@ decodeModuleName = fmap LF.ModuleName . decodeTypeList decodeText
233236
--------------------
234237
-- Module Exports --
235238
--------------------
239+
-- Re-exports use the `exports` label for backwards compatibility
240+
reExportName :: Integer -> LF.ExprValName
241+
reExportName i = LF.ExprValName $ "$$export" <> T.pack (show i)
242+
243+
unReExportName :: LF.ExprValName -> Maybe Integer
244+
unReExportName (LF.ExprValName name) = do
245+
suffix <- T.stripPrefix "$$export" name
246+
readMay (T.unpack suffix)
247+
248+
-- Full exports use $$explicitExport<n>, with the `$$explicitExports` tag to differentiate between
249+
-- no explicit exports, and old style exporting (i.e. everything exported)
236250
exportName :: Integer -> LF.ExprValName
237-
exportName i = LF.ExprValName $ "$$export" <> T.pack (show i)
251+
exportName i = LF.ExprValName $ "$$explicitExport" <> T.pack (show i)
238252

239253
unExportName :: LF.ExprValName -> Maybe Integer
240254
unExportName (LF.ExprValName name) = do
241-
suffix <- T.stripPrefix "$$export" name
255+
suffix <- T.stripPrefix "$$explicitExport" name
242256
readMay (T.unpack suffix)
243257

258+
explicitExportsTag :: LF.ExprValName
259+
explicitExportsTag = LF.ExprValName "$$explicitExports"
260+
244261
newtype QualName = QualName (LF.Qualified GHC.OccName)
245262
deriving (Eq)
246263

Diff for: sdk/compiler/damlc/daml-opts/daml-opts-types/DA/Daml/Options/Types.hs

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module DA.Daml.Options.Types
2222
, ModRenaming(..)
2323
, PackageArg(..)
2424
, ErrorOrWarning
25+
, IgnoreDataDepVisibility(..)
2526
, defaultOptions
2627
, damlArtifactDir
2728
, projectPackageDatabase
@@ -145,6 +146,7 @@ data Options = Options
145146
-- unit-id, as script + scenario service assume it will be "main"
146147
, optUpgradeInfo :: UpgradeInfo
147148
, optDamlWarningFlags :: WarningFlags.DamlWarningFlags ErrorOrWarning
149+
, optIgnoreDataDepVisibility :: IgnoreDataDepVisibility
148150
}
149151

150152
type ErrorOrWarning = Either TypeCheckerError.ErrorOrWarning LFConversion.ErrorOrWarning
@@ -161,6 +163,9 @@ newtype IncrementalBuild = IncrementalBuild { getIncrementalBuild :: Bool }
161163
newtype IgnorePackageMetadata = IgnorePackageMetadata { getIgnorePackageMetadata :: Bool }
162164
deriving Show
163165

166+
newtype IgnoreDataDepVisibility = IgnoreDataDepVisibility { getIgnoreDataDepVisibility :: Bool }
167+
deriving Show
168+
164169
newtype Haddock = Haddock Bool
165170
deriving Show
166171

@@ -297,6 +302,7 @@ defaultOptions mbVersion =
297302
, optHideUnitId = False
298303
, optUpgradeInfo = defaultUpgradeInfo
299304
, optDamlWarningFlags = WarningFlags.mkDamlWarningFlags damlWarningFlagParser []
305+
, optIgnoreDataDepVisibility = IgnoreDataDepVisibility False
300306
}
301307

302308
defaultUpgradeInfo :: UpgradeInfo

Diff for: sdk/compiler/damlc/lib/DA/Cli/Damlc/Packaging.hs

+16-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import qualified Control.Monad.Trans.State.Strict as State
2323
import qualified Data.ByteString as BS
2424
import Data.Either.Combinators (whenLeft)
2525
import Data.Graph (Graph, Vertex, graphFromEdges, reachable, topSort, transposeG, vertices)
26-
import Data.List.Extra ((\\), intercalate, nubSortOn)
26+
import Data.List.Extra ((\\), intercalate, nubSortOn, nubOrd)
2727
import qualified Data.Map.Strict as MS
2828
import Data.Maybe (fromMaybe, isNothing)
2929
import qualified Data.NameMap as NM
@@ -307,6 +307,7 @@ installDataDep InstallDataDepArgs {..} = do
307307
, configStablePackages = MS.fromList [ (LF.dalfPackageId dalfPkg, unitId) | ((unitId, _), dalfPkg) <- MS.toList stablePkgs ]
308308
, configDependencyInfo = dependencyInfo
309309
, configSdkPrefix = [T.pack currentSdkPrefix]
310+
, configIgnoreExplicitExports = getIgnoreDataDepVisibility $ optIgnoreDataDepVisibility opts
310311
}
311312

312313
pkg = LF.extPackagePkg (LF.dalfPackagePkg dalfPackage)
@@ -602,7 +603,7 @@ buildLfPackageGraph =
602603
, getDecodedDalfUnitId = decodedUnitId . snd
603604
, getDecodedDalfPkg = decodedDalfPkg . snd
604605
, getDalfPkgId = LF.dalfPackageId
605-
, getDalfPkgRefs = dalfPackageRefs
606+
, getDalfPkgRefs = nubOrd . dalfPackageRefs
606607
}
607608
where
608609
dalfPackageRefs :: LF.DalfPackage -> [LF.PackageId]
@@ -621,6 +622,19 @@ buildLfPackageGraph =
621622
, Just quals <- [LFC.decodeModuleImports ty]
622623
, LF.Qualified { LF.qualPackage } <- Set.toList quals
623624
]
625+
-- Pull out all package ids from re-exports as well
626+
<>
627+
[ qualPackage
628+
| m <- NM.toList $ LF.packageModules pkg
629+
, LF.DefValue {dvalBinder=(name, ty)} <- NM.toList $ LF.moduleValues m
630+
, Just _ <- [LFC.unReExportName name]
631+
, Just export <- [LFC.decodeExportInfo ty]
632+
, LFC.QualName (LF.Qualified { LF.qualPackage }) <-
633+
[ case export of
634+
LFC.ExportInfoVal name -> name
635+
LFC.ExportInfoTC name _ _ -> name
636+
]
637+
]
624638
, pid' /= pid
625639
]
626640

0 commit comments

Comments
 (0)