1*99e0aae7SDavid Rees# Copyright 2019 Google LLC 2*99e0aae7SDavid Rees# 3*99e0aae7SDavid Rees# Licensed under the Apache License, Version 2.0 (the "License"); 4*99e0aae7SDavid Rees# you may not use this file except in compliance with the License. 5*99e0aae7SDavid Rees# You may obtain a copy of the License at 6*99e0aae7SDavid Rees# 7*99e0aae7SDavid Rees# https://www.apache.org/licenses/LICENSE-2.0 8*99e0aae7SDavid Rees# 9*99e0aae7SDavid Rees# Unless required by applicable law or agreed to in writing, software 10*99e0aae7SDavid Rees# distributed under the License is distributed on an "AS IS" BASIS, 11*99e0aae7SDavid Rees# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*99e0aae7SDavid Rees# See the License for the specific language governing permissions and 13*99e0aae7SDavid Rees# limitations under the License. 14*99e0aae7SDavid Rees 15*99e0aae7SDavid Rees"""Main driver for the Emboss front-end. 16*99e0aae7SDavid Rees 17*99e0aae7SDavid ReesThe parse_emboss_file function performs a complete parse of the specified file, 18*99e0aae7SDavid Reesand returns an IR or formatted error message. 19*99e0aae7SDavid Rees""" 20*99e0aae7SDavid Rees 21*99e0aae7SDavid Reesimport collections 22*99e0aae7SDavid Rees 23*99e0aae7SDavid Reesfrom compiler.front_end import attribute_checker 24*99e0aae7SDavid Reesfrom compiler.front_end import constraints 25*99e0aae7SDavid Reesfrom compiler.front_end import dependency_checker 26*99e0aae7SDavid Reesfrom compiler.front_end import expression_bounds 27*99e0aae7SDavid Reesfrom compiler.front_end import lr1 28*99e0aae7SDavid Reesfrom compiler.front_end import module_ir 29*99e0aae7SDavid Reesfrom compiler.front_end import parser 30*99e0aae7SDavid Reesfrom compiler.front_end import symbol_resolver 31*99e0aae7SDavid Reesfrom compiler.front_end import synthetics 32*99e0aae7SDavid Reesfrom compiler.front_end import tokenizer 33*99e0aae7SDavid Reesfrom compiler.front_end import type_check 34*99e0aae7SDavid Reesfrom compiler.front_end import write_inference 35*99e0aae7SDavid Reesfrom compiler.util import error 36*99e0aae7SDavid Reesfrom compiler.util import ir_data 37*99e0aae7SDavid Reesfrom compiler.util import ir_data_utils 38*99e0aae7SDavid Reesfrom compiler.util import parser_types 39*99e0aae7SDavid Reesfrom compiler.util import resources 40*99e0aae7SDavid Rees 41*99e0aae7SDavid Rees_IrDebugInfo = collections.namedtuple("IrDebugInfo", ["ir", "debug_info", 42*99e0aae7SDavid Rees "errors"]) 43*99e0aae7SDavid Rees 44*99e0aae7SDavid Rees 45*99e0aae7SDavid Reesclass DebugInfo(object): 46*99e0aae7SDavid Rees """Debug information about Emboss parsing.""" 47*99e0aae7SDavid Rees __slots__ = ("modules") 48*99e0aae7SDavid Rees 49*99e0aae7SDavid Rees def __init__(self): 50*99e0aae7SDavid Rees self.modules = {} 51*99e0aae7SDavid Rees 52*99e0aae7SDavid Rees def __eq__(self, other): 53*99e0aae7SDavid Rees return self.modules == other.modules 54*99e0aae7SDavid Rees 55*99e0aae7SDavid Rees def __ne__(self, other): 56*99e0aae7SDavid Rees return not self == other 57*99e0aae7SDavid Rees 58*99e0aae7SDavid Rees 59*99e0aae7SDavid Reesclass ModuleDebugInfo(object): 60*99e0aae7SDavid Rees """Debug information about the parse of a single file. 61*99e0aae7SDavid Rees 62*99e0aae7SDavid Rees Attributes: 63*99e0aae7SDavid Rees file_name: The name of the file from which this module came. 64*99e0aae7SDavid Rees tokens: The tokenization of this module's source text. 65*99e0aae7SDavid Rees parse_tree: The raw parse tree for this module. 66*99e0aae7SDavid Rees ir: The intermediate representation of this module, before additional 67*99e0aae7SDavid Rees processing such as symbol resolution. 68*99e0aae7SDavid Rees used_productions: The set of grammar productions used when parsing this 69*99e0aae7SDavid Rees module. 70*99e0aae7SDavid Rees source_code: The source text of the module. 71*99e0aae7SDavid Rees """ 72*99e0aae7SDavid Rees __slots__ = ("file_name", "tokens", "parse_tree", "ir", "used_productions", 73*99e0aae7SDavid Rees "source_code") 74*99e0aae7SDavid Rees 75*99e0aae7SDavid Rees def __init__(self, file_name): 76*99e0aae7SDavid Rees self.file_name = file_name 77*99e0aae7SDavid Rees self.tokens = None 78*99e0aae7SDavid Rees self.parse_tree = None 79*99e0aae7SDavid Rees self.ir = None 80*99e0aae7SDavid Rees self.used_productions = None 81*99e0aae7SDavid Rees self.source_code = None 82*99e0aae7SDavid Rees 83*99e0aae7SDavid Rees def __eq__(self, other): 84*99e0aae7SDavid Rees return (self.file_name == other.file_name and self.tokens == other.tokens 85*99e0aae7SDavid Rees and self.parse_tree == other.parse_tree and self.ir == other.ir and 86*99e0aae7SDavid Rees self.used_productions == other.used_productions and 87*99e0aae7SDavid Rees self.source_code == other.source_code) 88*99e0aae7SDavid Rees 89*99e0aae7SDavid Rees def __ne__(self, other): 90*99e0aae7SDavid Rees return not self == other 91*99e0aae7SDavid Rees 92*99e0aae7SDavid Rees def format_tokenization(self): 93*99e0aae7SDavid Rees """Renders self.tokens in a human-readable format.""" 94*99e0aae7SDavid Rees return "\n".join([str(token) for token in self.tokens]) 95*99e0aae7SDavid Rees 96*99e0aae7SDavid Rees def format_parse_tree(self, parse_tree=None, indent=""): 97*99e0aae7SDavid Rees """Renders self.parse_tree in a human-readable format.""" 98*99e0aae7SDavid Rees if parse_tree is None: 99*99e0aae7SDavid Rees parse_tree = self.parse_tree 100*99e0aae7SDavid Rees result = [] 101*99e0aae7SDavid Rees if isinstance(parse_tree, lr1.Reduction): 102*99e0aae7SDavid Rees result.append(indent + parse_tree.symbol) 103*99e0aae7SDavid Rees if parse_tree.children: 104*99e0aae7SDavid Rees result.append(":\n") 105*99e0aae7SDavid Rees for child in parse_tree.children: 106*99e0aae7SDavid Rees result.append(self.format_parse_tree(child, indent + " ")) 107*99e0aae7SDavid Rees else: 108*99e0aae7SDavid Rees result.append("\n") 109*99e0aae7SDavid Rees else: 110*99e0aae7SDavid Rees result.append("{}{}\n".format(indent, parse_tree)) 111*99e0aae7SDavid Rees return "".join(result) 112*99e0aae7SDavid Rees 113*99e0aae7SDavid Rees def format_module_ir(self): 114*99e0aae7SDavid Rees """Renders self.ir in a human-readable format.""" 115*99e0aae7SDavid Rees return ir_data_utils.IrDataSerializer(self.ir).to_json(indent=2) 116*99e0aae7SDavid Rees 117*99e0aae7SDavid Rees 118*99e0aae7SDavid Reesdef format_production_set(productions): 119*99e0aae7SDavid Rees """Renders a set of productions in a human-readable format.""" 120*99e0aae7SDavid Rees return "\n".join([str(production) for production in sorted(productions)]) 121*99e0aae7SDavid Rees 122*99e0aae7SDavid Rees 123*99e0aae7SDavid Rees_cached_modules = {} 124*99e0aae7SDavid Rees 125*99e0aae7SDavid Rees 126*99e0aae7SDavid Reesdef parse_module_text(source_code, file_name): 127*99e0aae7SDavid Rees """Parses the text of a module, returning a module-level IR. 128*99e0aae7SDavid Rees 129*99e0aae7SDavid Rees Arguments: 130*99e0aae7SDavid Rees source_code: The text of the module to parse. 131*99e0aae7SDavid Rees file_name: The name of the module's source file (will be included in the 132*99e0aae7SDavid Rees resulting IR). 133*99e0aae7SDavid Rees 134*99e0aae7SDavid Rees Returns: 135*99e0aae7SDavid Rees A module-level intermediate representation (IR), prior to import and symbol 136*99e0aae7SDavid Rees resolution, and a corresponding ModuleDebugInfo, for debugging the parser. 137*99e0aae7SDavid Rees 138*99e0aae7SDavid Rees Raises: 139*99e0aae7SDavid Rees FrontEndFailure: An error occurred while parsing the module. str(error) 140*99e0aae7SDavid Rees will give a human-readable error message. 141*99e0aae7SDavid Rees """ 142*99e0aae7SDavid Rees # This is strictly an optimization to speed up tests, mostly by avoiding the 143*99e0aae7SDavid Rees # need to re-parse the prelude for every test .emb. 144*99e0aae7SDavid Rees if (source_code, file_name) in _cached_modules: 145*99e0aae7SDavid Rees debug_info = _cached_modules[source_code, file_name] 146*99e0aae7SDavid Rees ir = ir_data_utils.copy(debug_info.ir) 147*99e0aae7SDavid Rees else: 148*99e0aae7SDavid Rees debug_info = ModuleDebugInfo(file_name) 149*99e0aae7SDavid Rees debug_info.source_code = source_code 150*99e0aae7SDavid Rees tokens, errors = tokenizer.tokenize(source_code, file_name) 151*99e0aae7SDavid Rees if errors: 152*99e0aae7SDavid Rees return _IrDebugInfo(None, debug_info, errors) 153*99e0aae7SDavid Rees debug_info.tokens = tokens 154*99e0aae7SDavid Rees parse_result = parser.parse_module(tokens) 155*99e0aae7SDavid Rees if parse_result.error: 156*99e0aae7SDavid Rees return _IrDebugInfo( 157*99e0aae7SDavid Rees None, 158*99e0aae7SDavid Rees debug_info, 159*99e0aae7SDavid Rees [error.make_error_from_parse_error(file_name, parse_result.error)]) 160*99e0aae7SDavid Rees debug_info.parse_tree = parse_result.parse_tree 161*99e0aae7SDavid Rees used_productions = set() 162*99e0aae7SDavid Rees ir = module_ir.build_ir(parse_result.parse_tree, used_productions) 163*99e0aae7SDavid Rees ir.source_text = source_code 164*99e0aae7SDavid Rees debug_info.used_productions = used_productions 165*99e0aae7SDavid Rees debug_info.ir = ir_data_utils.copy(ir) 166*99e0aae7SDavid Rees _cached_modules[source_code, file_name] = debug_info 167*99e0aae7SDavid Rees ir.source_file_name = file_name 168*99e0aae7SDavid Rees return _IrDebugInfo(ir, debug_info, []) 169*99e0aae7SDavid Rees 170*99e0aae7SDavid Rees 171*99e0aae7SDavid Reesdef parse_module(file_name, file_reader): 172*99e0aae7SDavid Rees """Parses a module, returning a module-level IR. 173*99e0aae7SDavid Rees 174*99e0aae7SDavid Rees Arguments: 175*99e0aae7SDavid Rees file_name: The name of the module's source file. 176*99e0aae7SDavid Rees file_reader: A callable that returns either: 177*99e0aae7SDavid Rees (file_contents, None) or 178*99e0aae7SDavid Rees (None, list_of_error_detail_strings) 179*99e0aae7SDavid Rees 180*99e0aae7SDavid Rees Returns: 181*99e0aae7SDavid Rees (ir, debug_info, errors), where ir is a module-level intermediate 182*99e0aae7SDavid Rees representation (IR), debug_info is a ModuleDebugInfo containing the 183*99e0aae7SDavid Rees tokenization, parse tree, and original source text of all modules, and 184*99e0aae7SDavid Rees errors is a list of tokenization or parse errors. If errors is not an empty 185*99e0aae7SDavid Rees list, ir will be None. 186*99e0aae7SDavid Rees 187*99e0aae7SDavid Rees Raises: 188*99e0aae7SDavid Rees FrontEndFailure: An error occurred while reading or parsing the module. 189*99e0aae7SDavid Rees str(error) will give a human-readable error message. 190*99e0aae7SDavid Rees """ 191*99e0aae7SDavid Rees source_code, errors = file_reader(file_name) 192*99e0aae7SDavid Rees if errors: 193*99e0aae7SDavid Rees location = parser_types.make_location((1, 1), (1, 1)) 194*99e0aae7SDavid Rees return None, None, [ 195*99e0aae7SDavid Rees [error.error(file_name, location, "Unable to read file.")] + 196*99e0aae7SDavid Rees [error.note(file_name, location, e) for e in errors] 197*99e0aae7SDavid Rees ] 198*99e0aae7SDavid Rees return parse_module_text(source_code, file_name) 199*99e0aae7SDavid Rees 200*99e0aae7SDavid Rees 201*99e0aae7SDavid Reesdef get_prelude(): 202*99e0aae7SDavid Rees """Returns the module IR and debug info of the Emboss Prelude.""" 203*99e0aae7SDavid Rees return parse_module_text( 204*99e0aae7SDavid Rees resources.load("compiler.front_end", "prelude.emb"), "") 205*99e0aae7SDavid Rees 206*99e0aae7SDavid Rees 207*99e0aae7SDavid Reesdef parse_emboss_file(file_name, file_reader, stop_before_step=None): 208*99e0aae7SDavid Rees """Fully parses an .emb, and returns an IR suitable for passing to a back end. 209*99e0aae7SDavid Rees 210*99e0aae7SDavid Rees parse_emboss_file is a convenience function which calls only_parse_emboss_file 211*99e0aae7SDavid Rees and process_ir. 212*99e0aae7SDavid Rees 213*99e0aae7SDavid Rees Arguments: 214*99e0aae7SDavid Rees file_name: The name of the module's source file. 215*99e0aae7SDavid Rees file_reader: A callable that returns the contents of files, or raises 216*99e0aae7SDavid Rees IOError. 217*99e0aae7SDavid Rees stop_before_step: If set, parse_emboss_file will stop normalizing the IR 218*99e0aae7SDavid Rees just before the specified step. This parameter should be None for 219*99e0aae7SDavid Rees non-test code. 220*99e0aae7SDavid Rees 221*99e0aae7SDavid Rees Returns: 222*99e0aae7SDavid Rees (ir, debug_info, errors), where ir is a complete IR, ready for consumption 223*99e0aae7SDavid Rees by an Emboss back end, debug_info is a DebugInfo containing the 224*99e0aae7SDavid Rees tokenization, parse tree, and original source text of all modules, and 225*99e0aae7SDavid Rees errors is a list of tokenization or parse errors. If errors is not an empty 226*99e0aae7SDavid Rees list, ir will be None. 227*99e0aae7SDavid Rees """ 228*99e0aae7SDavid Rees ir, debug_info, errors = only_parse_emboss_file(file_name, file_reader) 229*99e0aae7SDavid Rees if errors: 230*99e0aae7SDavid Rees return _IrDebugInfo(None, debug_info, errors) 231*99e0aae7SDavid Rees ir, errors = process_ir(ir, stop_before_step) 232*99e0aae7SDavid Rees if errors: 233*99e0aae7SDavid Rees return _IrDebugInfo(None, debug_info, errors) 234*99e0aae7SDavid Rees return _IrDebugInfo(ir, debug_info, errors) 235*99e0aae7SDavid Rees 236*99e0aae7SDavid Rees 237*99e0aae7SDavid Reesdef only_parse_emboss_file(file_name, file_reader): 238*99e0aae7SDavid Rees """Parses an .emb, and returns an IR suitable for process_ir. 239*99e0aae7SDavid Rees 240*99e0aae7SDavid Rees only_parse_emboss_file parses the given file and all of its transitive 241*99e0aae7SDavid Rees imports, and returns a first-stage intermediate representation, which can be 242*99e0aae7SDavid Rees passed to process_ir. 243*99e0aae7SDavid Rees 244*99e0aae7SDavid Rees Arguments: 245*99e0aae7SDavid Rees file_name: The name of the module's source file. 246*99e0aae7SDavid Rees file_reader: A callable that returns the contents of files, or raises 247*99e0aae7SDavid Rees IOError. 248*99e0aae7SDavid Rees 249*99e0aae7SDavid Rees Returns: 250*99e0aae7SDavid Rees (ir, debug_info, errors), where ir is an intermediate representation (IR), 251*99e0aae7SDavid Rees debug_info is a DebugInfo containing the tokenization, parse tree, and 252*99e0aae7SDavid Rees original source text of all modules, and errors is a list of tokenization or 253*99e0aae7SDavid Rees parse errors. If errors is not an empty list, ir will be None. 254*99e0aae7SDavid Rees """ 255*99e0aae7SDavid Rees file_queue = [file_name] 256*99e0aae7SDavid Rees files = {file_name} 257*99e0aae7SDavid Rees debug_info = DebugInfo() 258*99e0aae7SDavid Rees ir = ir_data.EmbossIr(module=[]) 259*99e0aae7SDavid Rees while file_queue: 260*99e0aae7SDavid Rees file_to_parse = file_queue[0] 261*99e0aae7SDavid Rees del file_queue[0] 262*99e0aae7SDavid Rees if file_to_parse: 263*99e0aae7SDavid Rees module, module_debug_info, errors = parse_module(file_to_parse, 264*99e0aae7SDavid Rees file_reader) 265*99e0aae7SDavid Rees else: 266*99e0aae7SDavid Rees module, module_debug_info, errors = get_prelude() 267*99e0aae7SDavid Rees if module_debug_info: 268*99e0aae7SDavid Rees debug_info.modules[file_to_parse] = module_debug_info 269*99e0aae7SDavid Rees if errors: 270*99e0aae7SDavid Rees return _IrDebugInfo(None, debug_info, errors) 271*99e0aae7SDavid Rees ir.module.extend([module]) # Proto supports extend but not append here. 272*99e0aae7SDavid Rees for import_ in module.foreign_import: 273*99e0aae7SDavid Rees if import_.file_name.text not in files: 274*99e0aae7SDavid Rees file_queue.append(import_.file_name.text) 275*99e0aae7SDavid Rees files.add(import_.file_name.text) 276*99e0aae7SDavid Rees return _IrDebugInfo(ir, debug_info, []) 277*99e0aae7SDavid Rees 278*99e0aae7SDavid Rees 279*99e0aae7SDavid Reesdef process_ir(ir, stop_before_step): 280*99e0aae7SDavid Rees """Turns a first-stage IR into a fully-processed IR. 281*99e0aae7SDavid Rees 282*99e0aae7SDavid Rees process_ir performs all of the semantic processing steps on `ir`: resolving 283*99e0aae7SDavid Rees symbols, checking dependencies, adding type annotations, normalizing 284*99e0aae7SDavid Rees attributes, etc. process_ir is generally meant to be called with the result 285*99e0aae7SDavid Rees of parse_emboss_file(), but in theory could be called with a first-stage 286*99e0aae7SDavid Rees intermediate representation (IR) from another source. 287*99e0aae7SDavid Rees 288*99e0aae7SDavid Rees Arguments: 289*99e0aae7SDavid Rees ir: The IR to process. This structure will be modified during processing. 290*99e0aae7SDavid Rees stop_before_step: If set, process_ir will stop normalizing the IR just 291*99e0aae7SDavid Rees before the specified step. This parameter should be None for non-test 292*99e0aae7SDavid Rees code. 293*99e0aae7SDavid Rees 294*99e0aae7SDavid Rees Returns: 295*99e0aae7SDavid Rees (ir, errors), where ir is a complete IR, ready for consumption by an Emboss 296*99e0aae7SDavid Rees back end, and errors is a list of compilation errors. If errors is not an 297*99e0aae7SDavid Rees empty list, ir will be None. 298*99e0aae7SDavid Rees """ 299*99e0aae7SDavid Rees passes = (synthetics.desugar, 300*99e0aae7SDavid Rees symbol_resolver.resolve_symbols, 301*99e0aae7SDavid Rees dependency_checker.find_dependency_cycles, 302*99e0aae7SDavid Rees dependency_checker.set_dependency_order, 303*99e0aae7SDavid Rees symbol_resolver.resolve_field_references, 304*99e0aae7SDavid Rees type_check.annotate_types, 305*99e0aae7SDavid Rees type_check.check_types, 306*99e0aae7SDavid Rees expression_bounds.compute_constants, 307*99e0aae7SDavid Rees attribute_checker.normalize_and_verify, 308*99e0aae7SDavid Rees constraints.check_constraints, 309*99e0aae7SDavid Rees write_inference.set_write_methods) 310*99e0aae7SDavid Rees assert stop_before_step in [None] + [f.__name__ for f in passes], ( 311*99e0aae7SDavid Rees "Bad value for stop_before_step.") 312*99e0aae7SDavid Rees # Some parts of the IR are synthesized from "natural" parts of the IR, before 313*99e0aae7SDavid Rees # the natural parts have been fully error checked. Because of this, the 314*99e0aae7SDavid Rees # synthesized parts can have errors; in a couple of cases, they can have 315*99e0aae7SDavid Rees # errors that show up in an earlier pass than the errors in the natural parts 316*99e0aae7SDavid Rees # of the IR. As an example: 317*99e0aae7SDavid Rees # 318*99e0aae7SDavid Rees # struct Foo: 319*99e0aae7SDavid Rees # 0 [+1] bits: 320*99e0aae7SDavid Rees # 0 [+1] Flag flag 321*99e0aae7SDavid Rees # 1 [+flag] UInt:8 field 322*99e0aae7SDavid Rees # 323*99e0aae7SDavid Rees # In this case, the use of `flag` as the size of `field` is incorrect, because 324*99e0aae7SDavid Rees # `flag` is a boolean, but the size of a field must be an integer. 325*99e0aae7SDavid Rees # 326*99e0aae7SDavid Rees # Type checking occurs in two passes: in the first pass, expressions are 327*99e0aae7SDavid Rees # checked for internal consistency. In the second pass, expression types are 328*99e0aae7SDavid Rees # checked against their location. The use of `flag` would be caught in the 329*99e0aae7SDavid Rees # second pass. 330*99e0aae7SDavid Rees # 331*99e0aae7SDavid Rees # However, the generated_fields pass will synthesize a $size_in_bytes virtual 332*99e0aae7SDavid Rees # field that would look like: 333*99e0aae7SDavid Rees # 334*99e0aae7SDavid Rees # struct Foo: 335*99e0aae7SDavid Rees # 0 [+1] bits: 336*99e0aae7SDavid Rees # 0 [+1] Flag flag 337*99e0aae7SDavid Rees # 1 [+flag] UInt:8 field 338*99e0aae7SDavid Rees # let $size_in_bytes = $max(true ? 0 + 1 : 0, true ? 1 + flag : 0) 339*99e0aae7SDavid Rees # 340*99e0aae7SDavid Rees # Since `1 + flag` is not internally consistent, this type error would be 341*99e0aae7SDavid Rees # caught in the first pass, and the user would see a very strange error 342*99e0aae7SDavid Rees # message that "the right-hand argument of operator `+` must be an integer." 343*99e0aae7SDavid Rees # 344*99e0aae7SDavid Rees # In order to avoid showing these kinds of errors to the user, we defer any 345*99e0aae7SDavid Rees # errors in synthetic parts of the IR. Unless there is a compiler bug, those 346*99e0aae7SDavid Rees # errors will show up as errors in the natural parts of the IR, which should 347*99e0aae7SDavid Rees # be much more comprehensible to end users. 348*99e0aae7SDavid Rees # 349*99e0aae7SDavid Rees # If, for some reason, there is an error in the synthetic IR, but no error in 350*99e0aae7SDavid Rees # the natural IR, the synthetic errors will be shown. In this case, the 351*99e0aae7SDavid Rees # formatting for the synthetic errors will show '[compiler bug]' for the 352*99e0aae7SDavid Rees # error location, which (hopefully) will provide the end user with a cue that 353*99e0aae7SDavid Rees # the error is a compiler bug. 354*99e0aae7SDavid Rees deferred_errors = [] 355*99e0aae7SDavid Rees for function in passes: 356*99e0aae7SDavid Rees if stop_before_step == function.__name__: 357*99e0aae7SDavid Rees return (ir, []) 358*99e0aae7SDavid Rees errors, hidden_errors = error.split_errors(function(ir)) 359*99e0aae7SDavid Rees if errors: 360*99e0aae7SDavid Rees return (None, errors) 361*99e0aae7SDavid Rees deferred_errors.extend(hidden_errors) 362*99e0aae7SDavid Rees 363*99e0aae7SDavid Rees if deferred_errors: 364*99e0aae7SDavid Rees return (None, deferred_errors) 365*99e0aae7SDavid Rees 366*99e0aae7SDavid Rees assert stop_before_step is None, "Bad value for stop_before_step." 367*99e0aae7SDavid Rees return (ir, []) 368