1- use std:: collections:: BTreeMap ;
1+ use std:: collections:: { BTreeMap , BTreeSet } ;
22use std:: fs:: { self , File } ;
33use std:: io:: Write ;
44use std:: path:: { Path , PathBuf } ;
@@ -183,21 +183,52 @@ fn convert_module_list(
183183
184184 let mut res = vec ! [ ] ;
185185 let mut module_tree = ModuleTree ( BTreeMap :: new ( ) ) ;
186+ // A C file can have the same stem as a sibling directory, such as
187+ // `hash.c` and `hash/sha1.c`. The current nested module emitter cannot
188+ // represent both at `mod hash`, so emit the file module with an explicit
189+ // path and keep the directory in the nested tree.
190+ let internal_modules = modules
191+ . iter ( )
192+ . filter_map ( |m| {
193+ if tcfg. is_binary ( m) {
194+ return None ;
195+ }
196+ let relpath = m. strip_prefix ( build_dir) . ok ( ) ?;
197+ let path = module_path ( relpath) ;
198+ Some ( ( m, path) )
199+ } )
200+ . collect :: < Vec < _ > > ( ) ;
201+ let collision_modules = internal_modules
202+ . iter ( )
203+ . filter_map ( |( module, path) | {
204+ internal_modules
205+ . iter ( )
206+ . any ( |( _, other) | other. len ( ) > path. len ( ) && other. starts_with ( path) )
207+ . then_some ( * module)
208+ } )
209+ . collect :: < BTreeSet < _ > > ( ) ;
210+ let mut used_flat_names = BTreeSet :: new ( ) ;
211+
186212 for m in & modules {
187213 match m. strip_prefix ( build_dir) {
188- Ok ( relpath) if !tcfg. is_binary ( m) => {
214+ Ok ( relpath) if !tcfg. is_binary ( m) && !collision_modules . contains ( m ) => {
189215 // The module is inside the build directory, use nested modules
190216 let mut cur = & mut module_tree;
191- for sm in relpath. iter ( ) {
192- let path = Path :: new ( sm) ;
193- let name = get_module_name ( path, true , false , false ) . unwrap ( ) ;
217+ for name in module_path ( relpath) {
194218 cur = cur. 0 . entry ( name) . or_default ( ) ;
195219 }
196220 }
197221 _ => {
198222 let relpath = diff_paths ( m, build_dir) . unwrap ( ) ;
199223 let path = Some ( relpath. to_str ( ) . unwrap ( ) . to_string ( ) ) ;
200- let name = get_module_name ( m, true , false , false ) . unwrap ( ) ;
224+ let name = if collision_modules. contains ( m) {
225+ unique_collision_module_name (
226+ m. strip_prefix ( build_dir) . unwrap ( ) ,
227+ & mut used_flat_names,
228+ )
229+ } else {
230+ get_module_name ( m, true , false , false ) . unwrap ( )
231+ } ;
201232 res. push ( Module {
202233 path,
203234 name,
@@ -211,6 +242,30 @@ fn convert_module_list(
211242 res
212243}
213244
245+ fn module_path ( path : & Path ) -> Vec < String > {
246+ path. iter ( )
247+ . map ( |component| {
248+ let path = Path :: new ( component) ;
249+ get_module_name ( path, true , false , false ) . unwrap ( )
250+ } )
251+ . collect ( )
252+ }
253+
254+ fn unique_collision_module_name ( path : & Path , used_names : & mut BTreeSet < String > ) -> String {
255+ let base = format ! ( "c2rust_{}" , module_path( path) . into_iter( ) . join( "_" ) ) ;
256+ if used_names. insert ( base. clone ( ) ) {
257+ return base;
258+ }
259+
260+ for i in 1 .. {
261+ let candidate = format ! ( "{base}_{i}" ) ;
262+ if used_names. insert ( candidate. clone ( ) ) {
263+ return candidate;
264+ }
265+ }
266+ unreachable ! ( )
267+ }
268+
214269fn emit_thin_binaries (
215270 tcfg : & TranspilerConfig ,
216271 build_dir : & Path ,
0 commit comments