• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/transform/promote_initializers_to_const_var.h"
16 
17 #include <utility>
18 
19 #include "src/program_builder.h"
20 #include "src/sem/block_statement.h"
21 #include "src/sem/call.h"
22 #include "src/sem/expression.h"
23 #include "src/sem/statement.h"
24 #include "src/sem/type_constructor.h"
25 
26 TINT_INSTANTIATE_TYPEINFO(tint::transform::PromoteInitializersToConstVar);
27 
28 namespace tint {
29 namespace transform {
30 
31 PromoteInitializersToConstVar::PromoteInitializersToConstVar() = default;
32 
33 PromoteInitializersToConstVar::~PromoteInitializersToConstVar() = default;
34 
Run(CloneContext & ctx,const DataMap &,DataMap &)35 void PromoteInitializersToConstVar::Run(CloneContext& ctx,
36                                         const DataMap&,
37                                         DataMap&) {
38   // Scan the AST nodes for array and structure initializers which
39   // need to be promoted to their own constant declaration.
40 
41   // Note: Correct handling of nested expressions is guaranteed due to the
42   // depth-first traversal of the ast::Node::Clone() methods:
43   //
44   // The inner-most initializers are traversed first, and they are hoisted
45   // to const variables declared just above the statement of use. The outer
46   // initializer will then be hoisted, inserting themselves between the
47   // inner declaration and the statement of use. This pattern applies correctly
48   // to any nested depth.
49   //
50   // Depth-first traversal of the AST is guaranteed because AST nodes are fully
51   // immutable and require their children to be constructed first so their
52   // pointer can be passed to the parent's constructor.
53 
54   for (auto* src_node : ctx.src->ASTNodes().Objects()) {
55     if (auto* src_init = src_node->As<ast::CallExpression>()) {
56       auto* call = ctx.src->Sem().Get(src_init);
57       if (!call->Target()->Is<sem::TypeConstructor>()) {
58         continue;
59       }
60       auto* src_sem_stmt = call->Stmt();
61       if (!src_sem_stmt) {
62         // Expression is outside of a statement. This usually means the
63         // expression is part of a global (module-scope) constant declaration.
64         // These must be constexpr, and so cannot contain the type of
65         // expressions that must be sanitized.
66         continue;
67       }
68       auto* src_stmt = src_sem_stmt->Declaration();
69 
70       if (auto* src_var_decl = src_stmt->As<ast::VariableDeclStatement>()) {
71         if (src_var_decl->variable->constructor == src_init) {
72           // This statement is just a variable declaration with the initializer
73           // as the constructor value. This is what we're attempting to
74           // transform to, and so ignore.
75           continue;
76         }
77       }
78 
79       auto* src_ty = call->Type();
80       if (src_ty->IsAnyOf<sem::Array, sem::Struct>()) {
81         // Create a new symbol for the constant
82         auto dst_symbol = ctx.dst->Sym();
83         // Clone the type
84         auto* dst_ty = CreateASTTypeFor(ctx, call->Type());
85         // Clone the initializer
86         auto* dst_init = ctx.Clone(src_init);
87         // Construct the constant that holds the hoisted initializer
88         auto* dst_var = ctx.dst->Const(dst_symbol, dst_ty, dst_init);
89         // Construct the variable declaration statement
90         auto* dst_var_decl = ctx.dst->Decl(dst_var);
91         // Construct the identifier for referencing the constant
92         auto* dst_ident = ctx.dst->Expr(dst_symbol);
93 
94         // Insert the constant before the usage
95         ctx.InsertBefore(src_sem_stmt->Block()->Declaration()->statements,
96                          src_stmt, dst_var_decl);
97         // Replace the inlined initializer with a reference to the constant
98         ctx.Replace(src_init, dst_ident);
99       }
100     }
101   }
102 
103   ctx.Clone();
104 }
105 
106 }  // namespace transform
107 }  // namespace tint
108