@@ -767,6 +767,24 @@ package {name} is defined in two different locations:\n\
767767 }
768768 }
769769
770+ // Build a set of interfaces which are required to be imported because
771+ // of `into`'s exports. This set is then used below during
772+ // `ensure_can_add_world_export`.
773+ //
774+ // This is the set of interfaces which exports depend on that are
775+ // themselves not exports.
776+ let mut must_be_imported = HashMap :: new ( ) ;
777+ for ( key, export) in into_world. exports . iter ( ) {
778+ for dep in self . world_item_direct_deps ( export) {
779+ if into_world. exports . contains_key ( & WorldKey :: Interface ( dep) ) {
780+ continue ;
781+ }
782+ self . foreach_interface_dep ( dep, & mut |id| {
783+ must_be_imported. insert ( id, key. clone ( ) ) ;
784+ } ) ;
785+ }
786+ }
787+
770788 // Next walk over exports of `from` and process these similarly to
771789 // imports.
772790 for ( name, from_export) in from_world. exports . iter ( ) {
@@ -779,10 +797,15 @@ package {name} is defined in two different locations:\n\
779797 None => {
780798 // See comments in `ensure_can_add_world_export` for why
781799 // this is slightly different than imports.
782- self . ensure_can_add_world_export ( into_world, name, from_export)
783- . with_context ( || {
784- format ! ( "failed to add export `{}`" , self . name_world_key( name) )
785- } ) ?;
800+ self . ensure_can_add_world_export (
801+ into_world,
802+ name,
803+ from_export,
804+ & must_be_imported,
805+ )
806+ . with_context ( || {
807+ format ! ( "failed to add export `{}`" , self . name_world_key( name) )
808+ } ) ?;
786809 new_exports. push ( ( name. clone ( ) , from_export. clone ( ) ) ) ;
787810 }
788811 }
@@ -855,74 +878,99 @@ package {name} is defined in two different locations:\n\
855878 /// This method ensures that the world export of `name` and `item` can be
856879 /// added to the world `into` without changing the meaning of `into`.
857880 ///
858- /// This is somewhat tricky due to how exports/imports are elaborated today
859- /// but the basic idea is that the transitive dependencies of an `export`
860- /// will be implicitly `import`ed if they're not otherwise listed as
861- /// exports. That means that if a transitive dependency of a preexisting
862- /// export is added as a new export it might change the meaning of an
863- /// existing import if it was otherwise already hooked up to an import.
881+ /// All dependencies of world exports must either be:
882+ ///
883+ /// * An export themselves
884+ /// * An import with all transitive dependencies of the import also imported
864885 ///
865- /// This method rules out this situation.
886+ /// It's not possible to depend on an import which then also depends on an
887+ /// export at some point, for example. This method ensures that if `name`
888+ /// and `item` are added that this property is upheld.
866889 fn ensure_can_add_world_export (
867890 & self ,
868891 into : & World ,
869892 name : & WorldKey ,
870893 item : & WorldItem ,
894+ must_be_imported : & HashMap < InterfaceId , WorldKey > ,
871895 ) -> Result < ( ) > {
872896 assert ! ( !into. exports. contains_key( name) ) ;
873- let interface = match name {
874- // Top-level exports always depend on imports, so these are always
875- // allowed to be added.
876- WorldKey :: Name ( _) => return Ok ( ( ) ) ,
877-
878- // This is the case we're worried about. Here if the key is an
879- // interface then the item must also be an interface.
880- WorldKey :: Interface ( key) => {
881- match item {
882- WorldItem :: Interface { id, .. } => assert_eq ! ( id, key) ,
883- _ => unreachable ! ( ) ,
884- }
885- * key
886- }
887- } ;
897+ let name = self . name_world_key ( name) ;
888898
889- // For `interface` to be added as a new export of `into` then it must be
890- // the case that no previous export of `into` depends on `interface`.
891- // Test that by walking all interface exports and seeing if any types
892- // refer to this interface.
893- for ( export_name, export) in into. exports . iter ( ) {
894- let export_interface = match export_name {
895- WorldKey :: Name ( _) => continue ,
896- WorldKey :: Interface ( key) => {
897- match export {
898- WorldItem :: Interface { id, .. } => assert_eq ! ( id, key) ,
899- _ => unreachable ! ( ) ,
900- }
901- * key
902- }
903- } ;
904- assert ! ( export_interface != interface) ;
905- let iface = & self . interfaces [ export_interface] ;
906- for ( name, ty) in iface. types . iter ( ) {
907- let other_ty = match self . types [ * ty] . kind {
908- TypeDefKind :: Type ( Type :: Id ( ty) ) => ty,
909- _ => continue ,
910- } ;
911- if self . types [ other_ty] . owner != TypeOwner :: Interface ( interface) {
912- continue ;
913- }
899+ // First make sure that all of this item's dependencies are either
900+ // exported or the entire chain of imports rooted at that dependency are
901+ // all imported.
902+ for dep in self . world_item_direct_deps ( item) {
903+ if into. exports . contains_key ( & WorldKey :: Interface ( dep) ) {
904+ continue ;
905+ }
906+ self . ensure_not_exported ( into, dep)
907+ . with_context ( || format ! ( "failed validating export of `{name}`" ) ) ?;
908+ }
914909
915- let export_name = self . name_world_key ( export_name) ;
910+ // Second make sure that this item, if it's an interface, will not alter
911+ // the meaning of the preexisting world by ensuring that it's not in the
912+ // set of "must be imported" items.
913+ if let WorldItem :: Interface { id, .. } = item {
914+ if let Some ( export) = must_be_imported. get ( & id) {
915+ let export_name = self . name_world_key ( export) ;
916916 bail ! (
917- "export `{export_name}` has a type `{name}` which could \
918- change meaning if this world export were added"
919- )
917+ "export `{export_name}` depends on `{name}` \
918+ previously as an import which will change meaning \
919+ if `{name}` is added as an export"
920+ ) ;
920921 }
921922 }
922923
923924 Ok ( ( ) )
924925 }
925926
927+ fn ensure_not_exported ( & self , world : & World , id : InterfaceId ) -> Result < ( ) > {
928+ let key = WorldKey :: Interface ( id) ;
929+ let name = self . name_world_key ( & key) ;
930+ if world. exports . contains_key ( & key) {
931+ bail ! (
932+ "world exports `{name}` but it's also transitively used by an \
933+ import \
934+ which means that this is not valid"
935+ )
936+ }
937+ for dep in self . interface_direct_deps ( id) {
938+ self . ensure_not_exported ( world, dep)
939+ . with_context ( || format ! ( "failed validating transitive import dep `{name}`" ) ) ?;
940+ }
941+ Ok ( ( ) )
942+ }
943+
944+ /// Returns an iterator of all the direct interface dependencies of this
945+ /// `item`.
946+ ///
947+ /// Note that this doesn't include transitive dependencies, that must be
948+ /// followed manually.
949+ fn world_item_direct_deps ( & self , item : & WorldItem ) -> impl Iterator < Item = InterfaceId > + ' _ {
950+ let mut interface = None ;
951+ let mut ty = None ;
952+ match item {
953+ WorldItem :: Function ( _) => { }
954+ WorldItem :: Type ( id) => ty = Some ( * id) ,
955+ WorldItem :: Interface { id, .. } => interface = Some ( * id) ,
956+ }
957+
958+ interface
959+ . into_iter ( )
960+ . flat_map ( move |id| self . interface_direct_deps ( id) )
961+ . chain ( ty. and_then ( |t| self . type_interface_dep ( t) ) )
962+ }
963+
964+ /// Invokes `f` with `id` and all transitive interface dependencies of `id`.
965+ ///
966+ /// Note that `f` may be called with the same id multiple times.
967+ fn foreach_interface_dep ( & self , id : InterfaceId , f : & mut dyn FnMut ( InterfaceId ) ) {
968+ f ( id) ;
969+ for dep in self . interface_direct_deps ( id) {
970+ self . foreach_interface_dep ( dep, f) ;
971+ }
972+ }
973+
926974 /// Returns the ID of the specified `interface`.
927975 ///
928976 /// Returns `None` for unnamed interfaces.
@@ -950,16 +998,20 @@ package {name} is defined in two different locations:\n\
950998 // Collect the set of interfaces which are depended on by exports. Also
951999 // all imported types are assumed to stay so collect any interfaces
9521000 // they depend on.
953- let mut live_through_exports = IndexSet :: default ( ) ;
1001+ let mut live_through_exports = IndexSet :: new ( ) ;
9541002 for ( _, export) in self . worlds [ world_id] . exports . iter ( ) {
9551003 if let WorldItem :: Interface { id, .. } = export {
956- self . collect_interface_deps ( * id, & mut live_through_exports) ;
1004+ self . foreach_interface_dep ( * id, & mut |id| {
1005+ live_through_exports. insert ( id) ;
1006+ } )
9571007 }
9581008 }
9591009 for ( _, import) in self . worlds [ world_id] . imports . iter ( ) {
9601010 if let WorldItem :: Type ( ty) = import {
9611011 if let Some ( dep) = self . type_interface_dep ( * ty) {
962- self . collect_interface_deps ( dep, & mut live_through_exports) ;
1012+ self . foreach_interface_dep ( dep, & mut |id| {
1013+ live_through_exports. insert ( id) ;
1014+ } )
9631015 }
9641016 }
9651017 }
@@ -1017,15 +1069,6 @@ package {name} is defined in two different locations:\n\
10171069 Ok ( ( ) )
10181070 }
10191071
1020- fn collect_interface_deps ( & self , interface : InterfaceId , deps : & mut IndexSet < InterfaceId > ) {
1021- if !deps. insert ( interface) {
1022- return ;
1023- }
1024- for dep in self . interface_direct_deps ( interface) {
1025- self . collect_interface_deps ( dep, deps) ;
1026- }
1027- }
1028-
10291072 /// Returns the ID of the specified `name` within the `pkg`.
10301073 pub fn id_of_name ( & self , pkg : PackageId , name : & str ) -> String {
10311074 let package = & self . packages [ pkg] ;
@@ -1534,11 +1577,24 @@ package {name} is defined in two different locations:\n\
15341577 WorldItem :: Interface { id, .. } => {
15351578 for dep in self . interface_direct_deps ( * id) {
15361579 let dep_key = WorldKey :: Interface ( dep) ;
1537- if !world. exports . contains_key ( & dep_key) {
1538- self . assert_interface_and_all_deps_imported_and_not_exported (
1539- world, key, dep,
1540- ) ;
1580+ if world. exports . contains_key ( & dep_key) {
1581+ continue ;
15411582 }
1583+ self . foreach_interface_dep ( dep, & mut |dep| {
1584+ let dep_key = WorldKey :: Interface ( dep) ;
1585+ assert ! (
1586+ world. imports. contains_key( & dep_key) ,
1587+ "world should import {} (required by {})" ,
1588+ self . name_world_key( & dep_key) ,
1589+ self . name_world_key( key) ,
1590+ ) ;
1591+ assert ! (
1592+ !world. exports. contains_key( & dep_key) ,
1593+ "world should not export {} (required by {})" ,
1594+ self . name_world_key( & dep_key) ,
1595+ self . name_world_key( key) ,
1596+ ) ;
1597+ } ) ;
15421598 }
15431599 }
15441600
@@ -1605,30 +1661,6 @@ package {name} is defined in two different locations:\n\
16051661 }
16061662 }
16071663
1608- fn assert_interface_and_all_deps_imported_and_not_exported (
1609- & self ,
1610- world : & World ,
1611- key : & WorldKey ,
1612- id : InterfaceId ,
1613- ) {
1614- let dep_key = WorldKey :: Interface ( id) ;
1615- assert ! (
1616- world. imports. contains_key( & dep_key) ,
1617- "world should import {} (required by {})" ,
1618- self . name_world_key( & dep_key) ,
1619- self . name_world_key( key) ,
1620- ) ;
1621- assert ! (
1622- !world. exports. contains_key( & dep_key) ,
1623- "world should not export {} (required by {})" ,
1624- self . name_world_key( & dep_key) ,
1625- self . name_world_key( key) ,
1626- ) ;
1627- for dep in self . interface_direct_deps ( id) {
1628- self . assert_interface_and_all_deps_imported_and_not_exported ( world, key, dep) ;
1629- }
1630- }
1631-
16321664 fn include_stability ( & self , stability : & Stability , pkg_id : & PackageId ) -> Result < bool > {
16331665 Ok ( match stability {
16341666 Stability :: Unknown => true ,
0 commit comments