tint: Refactor Extensions / Enables.

* Extract ast::Enable::ExtensionKind to ast::Extension.
* Move the parsing out of ast::Enable and next to ast/extension.h
* Change the ast::Enable constructor to take the Extension, instead of
  a std::string. It's the WGSL parser's responsibility to parse, not the
  AST nodes.
* Add ProgramBuilder::Enable() helper.
* Keep ast::Module simple - keep track of the declared AST Enable nodes,
  don't do any deduplicating of the enabled extensions.
* Add the de-duplicated ast::Extensions to the sem::Module.
* Remove the kInternalExtensionForTesting enum value - we have kF16
  now, which can be used instead for testing.
* Rename kNoExtension to kNone.

Bug: tint:1472
Change-Id: I9af635e95d36991ea468e6e0bf6798bb50937edc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/90523
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl b/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl
deleted file mode 100644
index 9ed8f61..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2022 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Enable a void internal extension
-enable InternalExtensionForTesting;
-
-fn bar() {
-}
-
-@stage(fragment)
-fn main() -> @location(0) vec4<f32> {
-    var a : vec2<f32> = vec2<f32>();
-    bar();
-    return vec4<f32>(0.4, 0.4, 0.8, 1.0);
-}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.spvasm b/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.spvasm
deleted file mode 100644
index f07e733..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.spvasm
+++ /dev/null
@@ -1,47 +0,0 @@
-; SPIR-V
-; Version: 1.3
-; Generator: Google Tint Compiler; 0
-; Bound: 25
-; Schema: 0
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %main "main" %value
-               OpExecutionMode %main OriginUpperLeft
-               OpName %value "value"
-               OpName %bar "bar"
-               OpName %main_inner "main_inner"
-               OpName %a "a"
-               OpName %main "main"
-               OpDecorate %value Location 0
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-          %5 = OpConstantNull %v4float
-      %value = OpVariable %_ptr_Output_v4float Output %5
-       %void = OpTypeVoid
-          %6 = OpTypeFunction %void
-         %10 = OpTypeFunction %v4float
-    %v2float = OpTypeVector %float 2
-         %14 = OpConstantNull %v2float
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%float_0_400000006 = OpConstant %float 0.400000006
-%float_0_800000012 = OpConstant %float 0.800000012
-    %float_1 = OpConstant %float 1
-         %21 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1
-        %bar = OpFunction %void None %6
-          %9 = OpLabel
-               OpReturn
-               OpFunctionEnd
- %main_inner = OpFunction %v4float None %10
-         %12 = OpLabel
-          %a = OpVariable %_ptr_Function_v2float Function %14
-               OpStore %a %14
-         %17 = OpFunctionCall %void %bar
-               OpReturnValue %21
-               OpFunctionEnd
-       %main = OpFunction %void None %6
-         %23 = OpLabel
-         %24 = OpFunctionCall %v4float %main_inner
-               OpStore %value %24
-               OpReturn
-               OpFunctionEnd
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.wgsl b/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.wgsl
deleted file mode 100644
index 987fb57..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.wgsl
+++ /dev/null
@@ -1,11 +0,0 @@
-enable InternalExtensionForTesting;
-
-fn bar() {
-}
-
-@stage(fragment)
-fn main() -> @location(0) vec4<f32> {
-  var a : vec2<f32> = vec2<f32>();
-  bar();
-  return vec4<f32>(0.400000006, 0.400000006, 0.800000012, 1.0);
-}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl b/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl
deleted file mode 100644
index c20b453..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2022 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Enable a void internal extension for multiple times
-enable InternalExtensionForTesting;
-enable InternalExtensionForTesting;
-enable InternalExtensionForTesting;
-
-fn bar() {
-}
-
-@stage(fragment)
-fn main() -> @location(0) vec4<f32> {
-    var a : vec2<f32> = vec2<f32>();
-    bar();
-    return vec4<f32>(0.4, 0.4, 0.8, 1.0);
-}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.glsl b/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.glsl
deleted file mode 100644
index 995583f..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.glsl
+++ /dev/null
@@ -1,18 +0,0 @@
-#version 310 es
-precision mediump float;
-
-layout(location = 0) out vec4 value;
-void bar() {
-}
-
-vec4 tint_symbol() {
-  vec2 a = vec2(0.0f, 0.0f);
-  bar();
-  return vec4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
-}
-
-void main() {
-  vec4 inner_result = tint_symbol();
-  value = inner_result;
-  return;
-}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.hlsl b/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.hlsl
deleted file mode 100644
index 93c7fa7..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.hlsl
+++ /dev/null
@@ -1,19 +0,0 @@
-void bar() {
-}
-
-struct tint_symbol {
-  float4 value : SV_Target0;
-};
-
-float4 main_inner() {
-  float2 a = float2(0.0f, 0.0f);
-  bar();
-  return float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
-}
-
-tint_symbol main() {
-  const float4 inner_result = main_inner();
-  tint_symbol wrapper_result = (tint_symbol)0;
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.msl b/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.msl
deleted file mode 100644
index d7fde53..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.msl
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <metal_stdlib>
-
-using namespace metal;
-void bar() {
-}
-
-struct tint_symbol_1 {
-  float4 value [[color(0)]];
-};
-
-float4 tint_symbol_inner() {
-  float2 a = float2();
-  bar();
-  return float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
-}
-
-fragment tint_symbol_1 tint_symbol() {
-  float4 const inner_result = tint_symbol_inner();
-  tint_symbol_1 wrapper_result = {};
-  wrapper_result.value = inner_result;
-  return wrapper_result;
-}
-
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.spvasm b/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.spvasm
deleted file mode 100644
index f07e733..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.spvasm
+++ /dev/null
@@ -1,47 +0,0 @@
-; SPIR-V
-; Version: 1.3
-; Generator: Google Tint Compiler; 0
-; Bound: 25
-; Schema: 0
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Fragment %main "main" %value
-               OpExecutionMode %main OriginUpperLeft
-               OpName %value "value"
-               OpName %bar "bar"
-               OpName %main_inner "main_inner"
-               OpName %a "a"
-               OpName %main "main"
-               OpDecorate %value Location 0
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-%_ptr_Output_v4float = OpTypePointer Output %v4float
-          %5 = OpConstantNull %v4float
-      %value = OpVariable %_ptr_Output_v4float Output %5
-       %void = OpTypeVoid
-          %6 = OpTypeFunction %void
-         %10 = OpTypeFunction %v4float
-    %v2float = OpTypeVector %float 2
-         %14 = OpConstantNull %v2float
-%_ptr_Function_v2float = OpTypePointer Function %v2float
-%float_0_400000006 = OpConstant %float 0.400000006
-%float_0_800000012 = OpConstant %float 0.800000012
-    %float_1 = OpConstant %float 1
-         %21 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1
-        %bar = OpFunction %void None %6
-          %9 = OpLabel
-               OpReturn
-               OpFunctionEnd
- %main_inner = OpFunction %v4float None %10
-         %12 = OpLabel
-          %a = OpVariable %_ptr_Function_v2float Function %14
-               OpStore %a %14
-         %17 = OpFunctionCall %void %bar
-               OpReturnValue %21
-               OpFunctionEnd
-       %main = OpFunction %void None %6
-         %23 = OpLabel
-         %24 = OpFunctionCall %v4float %main_inner
-               OpStore %value %24
-               OpReturn
-               OpFunctionEnd
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.wgsl b/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.wgsl
deleted file mode 100644
index 987fb57..0000000
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_duplicated_InternalExtensionForTesting.wgsl.expected.wgsl
+++ /dev/null
@@ -1,11 +0,0 @@
-enable InternalExtensionForTesting;
-
-fn bar() {
-}
-
-@stage(fragment)
-fn main() -> @location(0) vec4<f32> {
-  var a : vec2<f32> = vec2<f32>();
-  bar();
-  return vec4<f32>(0.400000006, 0.400000006, 0.800000012, 1.0);
-}
diff --git a/test/tint/extensions/parsing/basic.wgsl b/test/tint/extensions/parsing/basic.wgsl
new file mode 100644
index 0000000..d1d3017
--- /dev/null
+++ b/test/tint/extensions/parsing/basic.wgsl
@@ -0,0 +1,7 @@
+// Enable a void internal extension
+enable f16;
+
+@stage(fragment)
+fn main() -> @location(0) vec4<f32> {
+    return vec4<f32>(0.1, 0.2, 0.3, 0.4);
+}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.glsl b/test/tint/extensions/parsing/basic.wgsl.expected.glsl
similarity index 61%
rename from test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.glsl
rename to test/tint/extensions/parsing/basic.wgsl.expected.glsl
index 995583f..b094b6e 100644
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.glsl
+++ b/test/tint/extensions/parsing/basic.wgsl.expected.glsl
@@ -2,13 +2,8 @@
 precision mediump float;
 
 layout(location = 0) out vec4 value;
-void bar() {
-}
-
 vec4 tint_symbol() {
-  vec2 a = vec2(0.0f, 0.0f);
-  bar();
-  return vec4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
+  return vec4(0.100000001f, 0.200000003f, 0.300000012f, 0.400000006f);
 }
 
 void main() {
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.hlsl b/test/tint/extensions/parsing/basic.wgsl.expected.hlsl
similarity index 67%
rename from test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.hlsl
rename to test/tint/extensions/parsing/basic.wgsl.expected.hlsl
index 93c7fa7..1af441a 100644
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.hlsl
+++ b/test/tint/extensions/parsing/basic.wgsl.expected.hlsl
@@ -1,14 +1,9 @@
-void bar() {
-}
-
 struct tint_symbol {
   float4 value : SV_Target0;
 };
 
 float4 main_inner() {
-  float2 a = float2(0.0f, 0.0f);
-  bar();
-  return float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
+  return float4(0.100000001f, 0.200000003f, 0.300000012f, 0.400000006f);
 }
 
 tint_symbol main() {
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.msl b/test/tint/extensions/parsing/basic.wgsl.expected.msl
similarity index 74%
rename from test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.msl
rename to test/tint/extensions/parsing/basic.wgsl.expected.msl
index d7fde53..dc91926 100644
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.msl
+++ b/test/tint/extensions/parsing/basic.wgsl.expected.msl
@@ -1,17 +1,12 @@
 #include <metal_stdlib>
 
 using namespace metal;
-void bar() {
-}
-
 struct tint_symbol_1 {
   float4 value [[color(0)]];
 };
 
 float4 tint_symbol_inner() {
-  float2 a = float2();
-  bar();
-  return float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
+  return float4(0.100000001f, 0.200000003f, 0.300000012f, 0.400000006f);
 }
 
 fragment tint_symbol_1 tint_symbol() {
diff --git a/test/tint/extensions/parsing/basic.wgsl.expected.spvasm b/test/tint/extensions/parsing/basic.wgsl.expected.spvasm
new file mode 100644
index 0000000..225da82
--- /dev/null
+++ b/test/tint/extensions/parsing/basic.wgsl.expected.spvasm
@@ -0,0 +1,36 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 19
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %value
+               OpExecutionMode %main OriginUpperLeft
+               OpName %value "value"
+               OpName %main_inner "main_inner"
+               OpName %main "main"
+               OpDecorate %value Location 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %5 = OpConstantNull %v4float
+      %value = OpVariable %_ptr_Output_v4float Output %5
+          %6 = OpTypeFunction %v4float
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0_200000003 = OpConstant %float 0.200000003
+%float_0_300000012 = OpConstant %float 0.300000012
+%float_0_400000006 = OpConstant %float 0.400000006
+         %13 = OpConstantComposite %v4float %float_0_100000001 %float_0_200000003 %float_0_300000012 %float_0_400000006
+       %void = OpTypeVoid
+         %14 = OpTypeFunction %void
+ %main_inner = OpFunction %v4float None %6
+          %8 = OpLabel
+               OpReturnValue %13
+               OpFunctionEnd
+       %main = OpFunction %void None %14
+         %17 = OpLabel
+         %18 = OpFunctionCall %v4float %main_inner
+               OpStore %value %18
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/extensions/parsing/basic.wgsl.expected.wgsl b/test/tint/extensions/parsing/basic.wgsl.expected.wgsl
new file mode 100644
index 0000000..c084b23
--- /dev/null
+++ b/test/tint/extensions/parsing/basic.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+enable f16;
+
+@stage(fragment)
+fn main() -> @location(0) vec4<f32> {
+  return vec4<f32>(0.100000001, 0.200000003, 0.300000012, 0.400000006);
+}
diff --git a/test/tint/extensions/parsing/duplicated_extensions.wgsl b/test/tint/extensions/parsing/duplicated_extensions.wgsl
new file mode 100644
index 0000000..d72f18d
--- /dev/null
+++ b/test/tint/extensions/parsing/duplicated_extensions.wgsl
@@ -0,0 +1,9 @@
+// Enable a void internal extension for multiple times
+enable f16;
+enable f16;
+enable f16;
+
+@stage(fragment)
+fn main() -> @location(0) vec4<f32> {
+    return vec4<f32>(0.1, 0.2, 0.3, 0.4);
+}
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.glsl b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.glsl
similarity index 61%
copy from test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.glsl
copy to test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.glsl
index 995583f..b094b6e 100644
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.glsl
+++ b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.glsl
@@ -2,13 +2,8 @@
 precision mediump float;
 
 layout(location = 0) out vec4 value;
-void bar() {
-}
-
 vec4 tint_symbol() {
-  vec2 a = vec2(0.0f, 0.0f);
-  bar();
-  return vec4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
+  return vec4(0.100000001f, 0.200000003f, 0.300000012f, 0.400000006f);
 }
 
 void main() {
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.hlsl b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.hlsl
similarity index 67%
copy from test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.hlsl
copy to test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.hlsl
index 93c7fa7..1af441a 100644
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.hlsl
+++ b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.hlsl
@@ -1,14 +1,9 @@
-void bar() {
-}
-
 struct tint_symbol {
   float4 value : SV_Target0;
 };
 
 float4 main_inner() {
-  float2 a = float2(0.0f, 0.0f);
-  bar();
-  return float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
+  return float4(0.100000001f, 0.200000003f, 0.300000012f, 0.400000006f);
 }
 
 tint_symbol main() {
diff --git a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.msl b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.msl
similarity index 74%
copy from test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.msl
copy to test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.msl
index d7fde53..dc91926 100644
--- a/test/tint/extensions/InternalExtensionForTesting/simple_with_InternalExtensionForTesting.wgsl.expected.msl
+++ b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.msl
@@ -1,17 +1,12 @@
 #include <metal_stdlib>
 
 using namespace metal;
-void bar() {
-}
-
 struct tint_symbol_1 {
   float4 value [[color(0)]];
 };
 
 float4 tint_symbol_inner() {
-  float2 a = float2();
-  bar();
-  return float4(0.400000006f, 0.400000006f, 0.800000012f, 1.0f);
+  return float4(0.100000001f, 0.200000003f, 0.300000012f, 0.400000006f);
 }
 
 fragment tint_symbol_1 tint_symbol() {
diff --git a/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.spvasm b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.spvasm
new file mode 100644
index 0000000..225da82
--- /dev/null
+++ b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.spvasm
@@ -0,0 +1,36 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 19
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %main "main" %value
+               OpExecutionMode %main OriginUpperLeft
+               OpName %value "value"
+               OpName %main_inner "main_inner"
+               OpName %main "main"
+               OpDecorate %value Location 0
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+          %5 = OpConstantNull %v4float
+      %value = OpVariable %_ptr_Output_v4float Output %5
+          %6 = OpTypeFunction %v4float
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0_200000003 = OpConstant %float 0.200000003
+%float_0_300000012 = OpConstant %float 0.300000012
+%float_0_400000006 = OpConstant %float 0.400000006
+         %13 = OpConstantComposite %v4float %float_0_100000001 %float_0_200000003 %float_0_300000012 %float_0_400000006
+       %void = OpTypeVoid
+         %14 = OpTypeFunction %void
+ %main_inner = OpFunction %v4float None %6
+          %8 = OpLabel
+               OpReturnValue %13
+               OpFunctionEnd
+       %main = OpFunction %void None %14
+         %17 = OpLabel
+         %18 = OpFunctionCall %v4float %main_inner
+               OpStore %value %18
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.wgsl b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.wgsl
new file mode 100644
index 0000000..be5f381
--- /dev/null
+++ b/test/tint/extensions/parsing/duplicated_extensions.wgsl.expected.wgsl
@@ -0,0 +1,8 @@
+enable f16;
+enable f16;
+enable f16;
+
+@stage(fragment)
+fn main() -> @location(0) vec4<f32> {
+  return vec4<f32>(0.100000001, 0.200000003, 0.300000012, 0.400000006);
+}