• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 17516257898

06 Sep 2025 03:26PM UTC coverage: 59.923% (+0.02%) from 59.904%
17516257898

push

github

web-flow
Fix handling of byref, rest, and default parameters in uninitialized functions (#814)

* Update grammar

* update prettifier

* Update semantic analysis
- Move checks for 'super' and non-static to semantic analyzer
- Add checks for byref and default

* Add, update tests

* update escript guide, add tests for examples in guide

* update core-changes

* Address Discord comments
- Update core-changes to explicitly talk about default parameters

71 of 73 new or added lines in 6 files covered. (97.26%)

1 existing line in 1 file now uncovered.

43899 of 73259 relevant lines covered (59.92%)

435957.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/pol-core/bscript/compiler/astbuilder/GeneratedFunctionBuilder.cpp
1
#include "GeneratedFunctionBuilder.h"
2

3
#include <list>
4
#include <ranges>
5
#include <set>
6

7
#include "bscript/compiler/Report.h"
8
#include "bscript/compiler/ast/Argument.h"
9
#include "bscript/compiler/ast/ArrayInitializer.h"
10
#include "bscript/compiler/ast/ClassDeclaration.h"
11
#include "bscript/compiler/ast/FunctionBody.h"
12
#include "bscript/compiler/ast/FunctionCall.h"
13
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
14
#include "bscript/compiler/ast/FunctionParameterList.h"
15
#include "bscript/compiler/ast/FunctionReference.h"
16
#include "bscript/compiler/ast/GeneratedFunction.h"
17
#include "bscript/compiler/ast/Identifier.h"
18
#include "bscript/compiler/ast/ReturnStatement.h"
19
#include "bscript/compiler/ast/SpreadElement.h"
20
#include "bscript/compiler/ast/UserFunction.h"
21
#include "bscript/compiler/ast/ValueConsumer.h"
22
#include "bscript/compiler/astbuilder/BuilderWorkspace.h"
23
#include "bscript/compiler/astbuilder/SimpleValueCloner.h"
24
#include "bscript/compiler/model/ClassLink.h"
25
#include "bscript/compiler/model/FunctionLink.h"
26
#include "bscript/compiler/model/ScopableName.h"
27

28
namespace Pol::Bscript::Compiler
29
{
30
GeneratedFunctionBuilder::GeneratedFunctionBuilder( const SourceFileIdentifier& loc,
177✔
31
                                                    BuilderWorkspace& workspace )
177✔
32
    : CompoundStatementBuilder( loc, workspace )
177✔
33
{
34
}
177✔
35

36
void GeneratedFunctionBuilder::super_function( std::unique_ptr<GeneratedFunction>& super )
151✔
37
{
38
  std::vector<UserFunction*> base_class_ctors;
151✔
39
  std::set<ClassDeclaration*> visited;
151✔
40
  std::list<std::shared_ptr<ClassLink>> to_visit;
151✔
41
  auto class_declaration = super->class_declaration();
151✔
42

43
  to_visit.insert( to_visit.end(), class_declaration->base_class_links.begin(),
151✔
44
                   class_declaration->base_class_links.end() );
45

46
  for ( auto to_link_itr = to_visit.begin(); to_link_itr != to_visit.end();
397✔
47
        to_link_itr = to_visit.erase( to_link_itr ) )
246✔
48
  {
49
    if ( auto base_cd = ( *to_link_itr )->class_declaration() )
246✔
50
    {
51
      if ( visited.find( base_cd ) != visited.end() )
246✔
52
      {
53
        continue;
×
54
      }
55
      visited.insert( base_cd );
246✔
56

57
      if ( auto constructor_link = base_cd->constructor_link )
492✔
58
      {
59
        if ( auto base_class_ctor = constructor_link->user_function() )
243✔
60
        {
61
          base_class_ctors.push_back( base_class_ctor );
243✔
62
        }
63
      }
246✔
64
    }
65
  }
66

67
  build( super, base_class_ctors );
151✔
68
}
151✔
69

70
void GeneratedFunctionBuilder::constructor_function(
26✔
71
    std::unique_ptr<GeneratedFunction>& constructor )
72
{
73
  std::set<ClassDeclaration*> visited;
26✔
74
  std::list<std::shared_ptr<ClassLink>> to_visit;
26✔
75
  auto class_declaration = constructor->class_declaration();
26✔
76
  UserFunction* base_class_ctor = nullptr;
26✔
77

78
  to_visit.insert( to_visit.end(), class_declaration->base_class_links.begin(),
26✔
79
                   class_declaration->base_class_links.end() );
80

81
  for ( auto to_link_itr = to_visit.begin(); to_link_itr != to_visit.end();
26✔
82
        to_link_itr = to_visit.erase( to_link_itr ) )
×
83
  {
84
    if ( auto base_cd = ( *to_link_itr )->class_declaration() )
26✔
85
    {
86
      if ( visited.find( base_cd ) != visited.end() )
26✔
87
      {
88
        continue;
×
89
      }
90
      visited.insert( base_cd );
26✔
91

92
      if ( auto constructor_link = base_cd->constructor_link )
52✔
93
      {
94
        if ( auto ctor = constructor_link->user_function() )
26✔
95
        {
96
          base_class_ctor = ctor;
26✔
97
          break;
26✔
98
        }
99
      }
26✔
100
    }
101
  }
102

103
  if ( base_class_ctor != nullptr )
26✔
104
  {
105
    build( constructor, { base_class_ctor } );
26✔
106
  }
107

108
  // Set the generated function as the class' constructor function.
109
  class_declaration->constructor_link =
110
      std::make_unique<FunctionLink>( constructor->source_location, "", true );
26✔
111
  class_declaration->constructor_link->link_to( constructor.get() );
26✔
112
}
26✔
113

114
void GeneratedFunctionBuilder::build( std::unique_ptr<GeneratedFunction>& function,
177✔
115
                                      std::vector<UserFunction*> base_class_ctors )
116
{
117
  const auto& loc = function->source_location;
177✔
118
  // If there are no base ctors, skip updating the function's parameters and
119
  // body, therefore a super function that is "invalid" will have no parameters
120
  // or body.
121
  if ( !base_class_ctors.empty() )
177✔
122
  {
123
    auto& function_parameters = function->child<FunctionParameterList>( 0 ).children;
177✔
124

125
    function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
177✔
126
        loc, ScopableName( ScopeName::None, "this" ), true /* byref */, false /* unused */,
354✔
127
        false /* uninit_default */, false /* rest */ ) );
177✔
128

129
    // Our super() alias'ed parameter can be a rest parameter only if the not-last
130
    // constructors are not variadic.
131
    bool can_use_rest = true;
177✔
132

133
    for ( const auto* base_class_ctor :
177✔
134
          base_class_ctors | std::views::take( base_class_ctors.size() - 1 ) )
420✔
135
    {
136
      if ( base_class_ctor->is_variadic() )
80✔
137
      {
138
        can_use_rest = false;
14✔
139
        break;
14✔
140
      }
141
    }
142

143
    std::set<ScopableName> visited_arg_names;
177✔
144

145
    for ( auto base_class_ctor : base_class_ctors )
446✔
146
    {
147
      add_base_constructor( function, base_class_ctor, visited_arg_names, can_use_rest );
269✔
148
    }
149
  }
177✔
150

151
  std::string desc;
177✔
152
  Node::describe_tree_to_indented( *function, desc, 0 );
177✔
153
  workspace.report.debug( loc, "Generated function: {}", desc );
177✔
154
}
177✔
155

156
void GeneratedFunctionBuilder::add_base_constructor( std::unique_ptr<GeneratedFunction>& super,
269✔
157
                                                     UserFunction* base_class_ctor,
158
                                                     std::set<ScopableName>& visited_arg_names,
159
                                                     bool can_use_rest )
160
{
161
  auto& function_parameters = super->child<FunctionParameterList>( 0 ).children;
269✔
162
  auto& body = super->child<FunctionBody>( 1 ).children;
269✔
163
  auto params = base_class_ctor->parameters();
269✔
164
  const auto& loc = super->source_location;
269✔
165
  auto call_arguments = std::vector<std::unique_ptr<Argument>>();
269✔
166

167
  bool first = true;
269✔
168

169
  for ( auto& param_ref : params )
729✔
170
  {
171
    auto& param = param_ref.get();
462✔
172
    auto param_scope = first                      ? ScopeName::None
462✔
173
                       : param.name.scope.empty() ? ScopeName( base_class_ctor->name )
193✔
174
                                                  : param.name.scope;
462✔
175

176
    auto param_name = ScopableName( param_scope, param.name.name );
462✔
177

178

179
    // Skip the first `this` parameter of the function declaration, as we've already added it.
180
    if ( !first && visited_arg_names.find( param_name ) == visited_arg_names.end() )
462✔
181
    {
182
      // If the base ctor parameter is a rest param, our super() function parameter
183
      // will _not_ be a rest, but a regular variable with a default [empty]
184
      // array value. This only applies if we _cannot_ use rest parameters.
185
      if ( param.rest && !can_use_rest )
190✔
186
      {
187
        function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
38✔
NEW
188
            loc, param_name, param.byref, param.unused, false /* uninit_default */,
×
189
            false /* rest */,
38✔
190
            std::make_unique<ArrayInitializer>( param.source_location,
76✔
191
                                                std::vector<std::unique_ptr<Expression>>() ) ) );
38✔
192
      }
193
      // If the base ctor parameter has a default value, our super() function
194
      // parameter will have the same default value.
195
      else if ( auto default_value = param.default_value() )
152✔
196
      {
197
        SimpleValueCloner cloner( report, default_value->source_location );
11✔
198

199
        // Value must be cloneable
200
        if ( auto final_argument = cloner.clone( *default_value ) )
11✔
201
        {
202
          function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
9✔
NEW
203
              loc, param_name, param.byref, false /* unused */, false /* uninit_default */,
×
204
              false /* rest */, std::move( final_argument ) ) );
18✔
205
        }
206
        else
207
        {
208
          report.error( loc,
2✔
209
                        "In construction of '{}': Unable to create argument from default for "
210
                        "parameter '{}'.\n"
211
                        "  See also: {}",
212
                        super->scoped_name(), param.name, param.source_location );
4✔
213
          return;
2✔
214
        }
11✔
215
      }
11✔
216
      else
217
      {
218
        // This super() alias'ed parameter is a rest parameter if the base
219
        // ctor's parameter is rest _and_ we can use a rest parameter.
220
        auto is_rest_param = param.rest && can_use_rest;
141✔
221

222
        function_parameters.push_back( std::make_unique<FunctionParameterDeclaration>(
141✔
223
            loc, param_name, param.byref, false /* unused */, false /* uninit_default */,
282✔
224
            is_rest_param ) );
225
      }
226
    }
227

228
    // By default, the function call argument inside this super()'s function
229
    // declaration will just be the super() alias'ed parameter.
230
    std::unique_ptr<Expression> call_argument =
231
        std::make_unique<Identifier>( param.source_location, param_name );
460✔
232

233
    // If the base ctor parameter is a rest param, the base_ctor() function
234
    // call will spread the super() function parameter -- an array -- into
235
    // the base constructor.
236
    if ( param.rest )
460✔
237
    {
238
      // Passing an element that is not spreadable (eg. a string) will result
239
      // in an empty array passed to the base constructor.
240
      call_argument = std::make_unique<SpreadElement>( param.source_location,
88✔
241
                                                       std::move( call_argument ), false );
132✔
242
    }
243

244
    call_arguments.insert( call_arguments.end(),
460✔
245
                           std::make_unique<Argument>( param.source_location, param.name,
920✔
246
                                                       std::move( call_argument ), param.rest ) );
460✔
247
    visited_arg_names.insert( param_name );
460✔
248
    first = false;
460✔
249
  }
464✔
250

251
  auto fc = std::make_unique<FunctionCall>(
252
      loc, base_class_ctor->name, ScopableName( base_class_ctor->name, base_class_ctor->name ),
534✔
253
      std::move( call_arguments ) );
534✔
254

255
  fc->function_link->link_to( base_class_ctor );
267✔
256

257
  auto value_consumer = std::make_unique<ValueConsumer>( fc->source_location, std::move( fc ) );
267✔
258
  body.push_back( std::move( value_consumer ) );
267✔
259
}
271✔
260
}  // namespace Pol::Bscript::Compiler
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc