Repository: bytecodealliance/wasm-tools
Pull request #1218 (2023-09-26 20:46:18)

Sort exported functions first in `Resolve`
This commit sorts `Resolve::exports` a bit more to place exported functions first. While not strictly necessary this makes it easier for bindings generators to consume because it means they'll visit exported functions first before exported interfaces, meaning that any references to types will be more likely to reach the imported interfaces they refer to, which is what's expected.


Patch:
diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs
index 2bd16e58..2c15a201 100644
--- a/crates/wit-parser/src/resolve.rs
+++ b/crates/wit-parser/src/resolve.rs
@@ -1240,6 +1240,32 @@ impl Remap {
             }
         }
 
+        // After all that sort functions in exports to come before interfaces in
+        // exports. This is not strictly required for correctness but make
+        // iterating over a world much easier for consumers. Exported functions
+        // are guaranteed to use types from either imported interfaces or
+        // imported types into the world itself. Currently there is no means by
+        // which an export function, at the root, can use types from any other
+        // exported interfaces (can't be modeled syntactically in WIT). This
+        // means that by placing all functions first it guarantees that visitors
+        // which visit imports first then exports will walk over types and
+        // references in the order of what they're actually using.
+        //
+        // For example if an interface is both imported and exported and an
+        // exported function uses a type from that interface, then a visitor
+        // should visit the imported interface, then the exported function, then
+        // the exported interface. That way tables about "where was this type
+        // defined" will be correct as the last-inserted item will be used and
+        // correctly account for this.
+        world.exports.sort_by(|_, a, _, b| {
+            let rank = |item: &WorldItem| match item {
+                WorldItem::Type(_) => unreachable!(),
+                WorldItem::Function(_) => 0,
+                WorldItem::Interface(_) => 1,
+            };
+            rank(a).cmp(&rank(b))
+        });
+
         log::trace!("imports = {:?}", world.imports);
         log::trace!("exports = {:?}", world.exports);
 


Test patch:
diff --git a/crates/wit-component/tests/components/exports/component.wat b/crates/wit-component/tests/components/exports/component.wat
index 3102c4d8..536ac4aa 100644
--- a/crates/wit-component/tests/components/exports/component.wat
+++ b/crates/wit-component/tests/components/exports/component.wat
@@ -61,10 +61,24 @@
   (core instance (;0;) (instantiate 0))
   (alias core export 0 "memory" (core memory (;0;)))
   (alias core export 0 "cabi_realloc" (core func (;0;)))
-  (type (;0;) (flags "a" "b" "c"))
-  (type (;1;) (func (param "x" 0)))
-  (alias core export 0 "bar#a" (core func (;1;)))
-  (func (;0;) (type 1) (canon lift (core func 1)))
+  (type (;0;) (func))
+  (alias core export 0 "a" (core func (;1;)))
+  (func (;0;) (type 0) (canon lift (core func 1)))
+  (export (;1;) "a" (func 0))
+  (type (;1;) (func (param "a" s8) (param "b" s16) (param "c" s32) (param "d" s64) (result string)))
+  (alias core export 0 "b" (core func (;2;)))
+  (alias core export 0 "cabi_post_b" (core func (;3;)))
+  (func (;2;) (type 1) (canon lift (core func 2) (memory 0) string-encoding=utf8 (post-return 3)))
+  (export (;3;) "b" (func 2))
+  (type (;2;) (tuple s8 s16 s32 s64))
+  (type (;3;) (func (result 2)))
+  (alias core export 0 "c" (core func (;4;)))
+  (func (;4;) (type 3) (canon lift (core func 4) (memory 0)))
+  (export (;5;) "c" (func 4))
+  (type (;4;) (flags "a" "b" "c"))
+  (type (;5;) (func (param "x" 4)))
+  (alias core export 0 "bar#a" (core func (;5;)))
+  (func (;6;) (type 5) (canon lift (core func 5)))
   (component (;0;)
     (type (;0;) (flags "a" "b" "c"))
     (import "import-type-x" (type (;1;) (eq 0)))
@@ -76,23 +90,23 @@
     (export (;1;) "a" (func 0) (func (type 5)))
   )
   (instance (;0;) (instantiate 0
-      (with "import-func-a" (func 0))
-      (with "import-type-x" (type 0))
+      (with "import-func-a" (func 6))
+      (with "import-type-x" (type 4))
     )
   )
   (export (;1;) "bar" (instance 0))
-  (type (;2;) (func))
-  (alias core export 0 "foo#a" (core func (;2;)))
-  (func (;1;) (type 2) (canon lift (core func 2)))
-  (type (;3;) (variant (case "a") (case "b" string) (case "c" s64)))
-  (type (;4;) (func (param "x" string) (result 3)))
-  (alias core export 0 "foo#b" (core func (;3;)))
-  (alias core export 0 "cabi_post_foo#b" (core func (;4;)))
-  (func (;2;) (type 4) (canon lift (core func 3) (memory 0) (realloc 0) string-encoding=utf8 (post-return 4)))
-  (type (;5;) (func (param "x" 3) (result string)))
-  (alias core export 0 "foo#c" (core func (;5;)))
-  (alias core export 0 "cabi_post_foo#c" (core func (;6;)))
-  (func (;3;) (type 5) (canon lift (core func 5) (memory 0) (realloc 0) string-encoding=utf8 (post-return 6)))
+  (type (;6;) (func))
+  (alias core export 0 "foo#a" (core func (;6;)))
+  (func (;7;) (type 6) (canon lift (core func 6)))
+  (type (;7;) (variant (case "a") (case "b" string) (case "c" s64)))
+  (type (;8;) (func (param "x" string) (result 7)))
+  (alias core export 0 "foo#b" (core func (;7;)))
+  (alias core export 0 "cabi_post_foo#b" (core func (;8;)))
+  (func (;8;) (type 8) (canon lift (core func 7) (memory 0) (realloc 0) string-encoding=utf8 (post-return 8)))
+  (type (;9;) (func (param "x" 7) (result string)))
+  (alias core export 0 "foo#c" (core func (;9;)))
+  (alias core export 0 "cabi_post_foo#c" (core func (;10;)))
+  (func (;9;) (type 9) (canon lift (core func 9) (memory 0) (realloc 0) string-encoding=utf8 (post-return 10)))
   (component (;1;)
     (type (;0;) (func))
     (import "import-func-a" (func (;0;) (type 0)))
@@ -112,27 +126,13 @@
     (export (;5;) "c" (func 2) (func (type 9)))
   )
   (instance (;2;) (instantiate 1
-      (with "import-func-a" (func 1))
-      (with "import-func-b" (func 2))
-      (with "import-func-c" (func 3))
-      (with "import-type-x" (type 3))
+      (with "import-func-a" (func 7))
+      (with "import-func-b" (func 8))
+      (with "import-func-c" (func 9))
+      (with "import-type-x" (type 7))
     )
   )
   (export (;3;) "foo" (instance 2))
-  (type (;6;) (func))
-  (alias core export 0 "a" (core func (;7;)))
-  (func (;4;) (type 6) (canon lift (core func 7)))
-  (export (;5;) "a" (func 4))
-  (type (;7;) (func (param "a" s8) (param "b" s16) (param "c" s32) (param "d" s64) (result string)))
-  (alias core export 0 "b" (core func (;8;)))
-  (alias core export 0 "cabi_post_b" (core func (;9;)))
-  (func (;6;) (type 7) (canon lift (core func 8) (memory 0) string-encoding=utf8 (post-return 9)))
-  (export (;7;) "b" (func 6))
-  (type (;8;) (tuple s8 s16 s32 s64))
-  (type (;9;) (func (result 8)))
-  (alias core export 0 "c" (core func (;10;)))
-  (func (;8;) (type 9) (canon lift (core func 10) (memory 0)))
-  (export (;9;) "c" (func 8))
   (@producers
     (processed-by "wit-component" "$CARGO_PKG_VERSION")
   )
diff --git a/crates/wit-component/tests/components/exports/component.wit.print b/crates/wit-component/tests/components/exports/component.wit.print
index 832dcb39..022edaa3 100644
--- a/crates/wit-component/tests/components/exports/component.wit.print
+++ b/crates/wit-component/tests/components/exports/component.wit.print
@@ -1,6 +1,9 @@
 package root:component
 
 world root {
+  export a: func()
+  export b: func(a: s8, b: s16, c: s32, d: s64) -> string
+  export c: func() -> tuple<s8, s16, s32, s64>
   export bar: interface {
     flags x {
       a,
@@ -23,7 +26,4 @@ world root {
 
     c: func(x: x) -> string
   }
-  export a: func()
-  export b: func(a: s8, b: s16, c: s32, d: s64) -> string
-  export c: func() -> tuple<s8, s16, s32, s64>
 }
diff --git a/crates/wit-component/tests/components/import-export/component.wat b/crates/wit-component/tests/components/import-export/component.wat
index 42080823..fb2643bd 100644
--- a/crates/wit-component/tests/components/import-export/component.wat
+++ b/crates/wit-component/tests/components/import-export/component.wat
@@ -89,13 +89,19 @@
       (with "" (instance 3))
     )
   )
-  (type (;1;) (func))
-  (alias core export 2 "bar#a" (core func (;3;)))
-  (func (;1;) (type 1) (canon lift (core func 3)))
-  (type (;2;) (func (result string)))
-  (alias core export 2 "bar#b" (core func (;4;)))
-  (alias core export 2 "cabi_post_bar#b" (core func (;5;)))
-  (func (;2;) (type 2) (canon lift (core func 4) (memory 0) string-encoding=utf8 (post-return 5)))
+  (type (;1;) (tuple string u32 string))
+  (type (;2;) (func (param "x" string) (result 1)))
+  (alias core export 2 "a" (core func (;3;)))
+  (alias core export 2 "cabi_post_a" (core func (;4;)))
+  (func (;1;) (type 2) (canon lift (core func 3) (memory 0) (realloc 1) string-encoding=utf8 (post-return 4)))
+  (export (;2;) "a" (func 1))
+  (type (;3;) (func))
+  (alias core export 2 "bar#a" (core func (;5;)))
+  (func (;3;) (type 3) (canon lift (core func 5)))
+  (type (;4;) (func (result string)))
+  (alias core export 2 "bar#b" (core func (;6;)))
+  (alias core export 2 "cabi_post_bar#b" (core func (;7;)))
+  (func (;4;) (type 4) (canon lift (core func 6) (memory 0) string-encoding=utf8 (post-return 7)))
   (component (;0;)
     (type (;0;) (func))
     (import "import-func-a" (func (;0;) (type 0)))
@@ -107,17 +113,11 @@
     (export (;3;) "b" (func 1) (func (type 3)))
   )
   (instance (;1;) (instantiate 0
-      (with "import-func-a" (func 1))
-      (with "import-func-b" (func 2))
+      (with "import-func-a" (func 3))
+      (with "import-func-b" (func 4))
     )
   )
   (export (;2;) "bar" (instance 1))
-  (type (;3;) (tuple string u32 string))
-  (type (;4;) (func (param "x" string) (result 3)))
-  (alias core export 2 "a" (core func (;6;)))
-  (alias core export 2 "cabi_post_a" (core func (;7;)))
-  (func (;3;) (type 4) (canon lift (core func 6) (memory 0) (realloc 1) string-encoding=utf8 (post-return 7)))
-  (export (;4;) "a" (func 3))
   (@producers
     (processed-by "wit-component" "$CARGO_PKG_VERSION")
   )
diff --git a/crates/wit-component/tests/components/import-export/component.wit.print b/crates/wit-component/tests/components/import-export/component.wit.print
index 8121619d..7535b741 100644
--- a/crates/wit-component/tests/components/import-export/component.wit.print
+++ b/crates/wit-component/tests/components/import-export/component.wit.print
@@ -5,10 +5,10 @@ world root {
     a: func() -> string
   }
 
+  export a: func(x: string) -> tuple<string, u32, string>
   export bar: interface {
     a: func()
 
     b: func() -> string
   }
-  export a: func(x: string) -> tuple<string, u32, string>
 }
diff --git a/crates/wit-component/tests/interfaces/doc-comments.wat b/crates/wit-component/tests/interfaces/doc-comments.wat
index be6e2ec0..7f3cd033 100644
--- a/crates/wit-component/tests/interfaces/doc-comments.wat
+++ b/crates/wit-component/tests/interfaces/doc-comments.wat
@@ -40,6 +40,7 @@
           (import "t" (type (;1;) (eq 0)))
           (type (;2;) (func))
           (import "imp" (func (;0;) (type 2)))
+          (export (;1;) "exp" (func (type 2)))
           (type (;3;)
             (instance
               (type (;0;) u32)
@@ -49,7 +50,6 @@
             )
           )
           (export (;0;) "i" (instance (type 3)))
-          (export (;1;) "exp" (func (type 2)))
         )
       )
       (export (;0;) (interface "foo:foo/coverage-world") (component (type 2)))
diff --git a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
index fced3e0d..a1ee7008 100644
--- a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
+++ b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
@@ -65,6 +65,8 @@ world coverage-world {
   /// world typedef docs
   type t = u32
 
+  /// world func export docs
+  export exp: func()
   /// world inline interface docs
   export i: interface {
     /// inline interface typedef docs
@@ -73,6 +75,4 @@ world coverage-world {
     /// inline interface func docs
     f: func()
   }
-  /// world func export docs
-  export exp: func()
 }
diff --git a/crates/wit-component/tests/interfaces/world-top-level.wat b/crates/wit-component/tests/interfaces/world-top-level.wat
index bcfbdc9f..901850f3 100644
--- a/crates/wit-component/tests/interfaces/world-top-level.wat
+++ b/crates/wit-component/tests/interfaces/world-top-level.wat
@@ -11,13 +11,13 @@
           (import "foo" (func (;0;) (type 1)))
           (type (;2;) (func (param "arg" u32)))
           (import "bar" (func (;1;) (type 2)))
-          (type (;3;)
+          (export (;2;) "foo2" (func (type 1)))
+          (type (;3;) (func (result u32)))
+          (export (;3;) "bar2" (func (type 3)))
+          (type (;4;)
             (instance)
           )
-          (export (;1;) "another-interface" (instance (type 3)))
-          (export (;2;) "foo2" (func (type 1)))
-          (type (;4;) (func (result u32)))
-          (export (;3;) "bar2" (func (type 4)))
+          (export (;1;) "another-interface" (instance (type 4)))
         )
       )
       (export (;0;) (interface "foo:foo/foo") (component (type 0)))
diff --git a/crates/wit-component/tests/interfaces/world-top-level.wit.print b/crates/wit-component/tests/interfaces/world-top-level.wit.print
index b21b9f8a..b5f4997a 100644
--- a/crates/wit-component/tests/interfaces/world-top-level.wit.print
+++ b/crates/wit-component/tests/interfaces/world-top-level.wit.print
@@ -6,10 +6,10 @@ world foo {
   import foo: func()
   import bar: func(arg: u32)
 
-  export another-interface: interface {
-  }
   export foo2: func()
   export bar2: func() -> u32
+  export another-interface: interface {
+  }
 }
 world just-import {
   import foo: func()
diff --git a/crates/wit-parser/tests/ui/parse-fail/duplicate-function-params.wit.result b/crates/wit-parser/tests/ui/parse-fail/duplicate-function-params.wit.result
index 8bfb8ad8..2cd3ebab 100644
--- a/crates/wit-parser/tests/ui/parse-fail/duplicate-function-params.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/duplicate-function-params.wit.result
@@ -2,4 +2,4 @@ param `foo` is defined more than once
      --> tests/ui/parse-fail/duplicate-function-params.wit:6:23
       |
     6 |   foo: func(foo: u32, foo: u64);
-      |                       ^--
+      |                       ^--
\ No newline at end of file