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"""Symbol resolver for Emboss IR. 16*99e0aae7SDavid Rees 17*99e0aae7SDavid ReesThe resolve_symbols function should be used to generate canonical resolutions 18*99e0aae7SDavid Reesfor all symbol references in an Emboss IR. 19*99e0aae7SDavid Rees""" 20*99e0aae7SDavid Rees 21*99e0aae7SDavid Reesimport collections 22*99e0aae7SDavid Rees 23*99e0aae7SDavid Reesfrom compiler.util import error 24*99e0aae7SDavid Reesfrom compiler.util import ir_data 25*99e0aae7SDavid Reesfrom compiler.util import ir_data_utils 26*99e0aae7SDavid Reesfrom compiler.util import ir_util 27*99e0aae7SDavid Reesfrom compiler.util import traverse_ir 28*99e0aae7SDavid Rees 29*99e0aae7SDavid Rees# TODO(bolms): Symbol resolution raises an exception at the first error, but 30*99e0aae7SDavid Rees# this is one place where it can make sense to report multiple errors. 31*99e0aae7SDavid Rees 32*99e0aae7SDavid ReesFileLocation = collections.namedtuple("FileLocation", ["file", "location"]) 33*99e0aae7SDavid Rees 34*99e0aae7SDavid Rees 35*99e0aae7SDavid Reesdef ambiguous_name_error(file_name, location, name, candidate_locations): 36*99e0aae7SDavid Rees """A name cannot be resolved because there are two or more candidates.""" 37*99e0aae7SDavid Rees result = [error.error(file_name, location, "Ambiguous name '{}'".format(name)) 38*99e0aae7SDavid Rees ] 39*99e0aae7SDavid Rees for location in sorted(candidate_locations): 40*99e0aae7SDavid Rees result.append(error.note(location.file, location.location, 41*99e0aae7SDavid Rees "Possible resolution")) 42*99e0aae7SDavid Rees return result 43*99e0aae7SDavid Rees 44*99e0aae7SDavid Rees 45*99e0aae7SDavid Reesdef duplicate_name_error(file_name, location, name, original_location): 46*99e0aae7SDavid Rees """A name is defined two or more times.""" 47*99e0aae7SDavid Rees return [error.error(file_name, location, "Duplicate name '{}'".format(name)), 48*99e0aae7SDavid Rees error.note(original_location.file, original_location.location, 49*99e0aae7SDavid Rees "Original definition")] 50*99e0aae7SDavid Rees 51*99e0aae7SDavid Rees 52*99e0aae7SDavid Reesdef missing_name_error(file_name, location, name): 53*99e0aae7SDavid Rees return [error.error(file_name, location, "No candidate for '{}'".format(name)) 54*99e0aae7SDavid Rees ] 55*99e0aae7SDavid Rees 56*99e0aae7SDavid Rees 57*99e0aae7SDavid Reesdef array_subfield_error(file_name, location, name): 58*99e0aae7SDavid Rees return [error.error(file_name, location, 59*99e0aae7SDavid Rees "Cannot access member of array '{}'".format(name))] 60*99e0aae7SDavid Rees 61*99e0aae7SDavid Rees 62*99e0aae7SDavid Reesdef noncomposite_subfield_error(file_name, location, name): 63*99e0aae7SDavid Rees return [error.error(file_name, location, 64*99e0aae7SDavid Rees "Cannot access member of noncomposite field '{}'".format( 65*99e0aae7SDavid Rees name))] 66*99e0aae7SDavid Rees 67*99e0aae7SDavid Rees 68*99e0aae7SDavid Reesdef _nested_name(canonical_name, name): 69*99e0aae7SDavid Rees """Creates a new CanonicalName with name appended to the object_path.""" 70*99e0aae7SDavid Rees return ir_data.CanonicalName( 71*99e0aae7SDavid Rees module_file=canonical_name.module_file, 72*99e0aae7SDavid Rees object_path=list(canonical_name.object_path) + [name]) 73*99e0aae7SDavid Rees 74*99e0aae7SDavid Rees 75*99e0aae7SDavid Reesclass _Scope(dict): 76*99e0aae7SDavid Rees """A _Scope holds data for a symbol. 77*99e0aae7SDavid Rees 78*99e0aae7SDavid Rees A _Scope is a dict with some additional attributes. Lexically nested names 79*99e0aae7SDavid Rees are kept in the dict, and bookkeeping is kept in the additional attributes. 80*99e0aae7SDavid Rees 81*99e0aae7SDavid Rees For example, each module should have a child _Scope for each type contained in 82*99e0aae7SDavid Rees the module. `struct` and `bits` types should have nested _Scopes for each 83*99e0aae7SDavid Rees field; `enum` types should have nested scopes for each enumerated name. 84*99e0aae7SDavid Rees 85*99e0aae7SDavid Rees Attributes: 86*99e0aae7SDavid Rees canonical_name: The absolute name of this symbol; e.g. ("file.emb", 87*99e0aae7SDavid Rees "TypeName", "SubTypeName", "field_name") 88*99e0aae7SDavid Rees source_location: The ir_data.SourceLocation where this symbol is defined. 89*99e0aae7SDavid Rees visibility: LOCAL, PRIVATE, or SEARCHABLE; see below. 90*99e0aae7SDavid Rees alias: If set, this name is merely a pointer to another name. 91*99e0aae7SDavid Rees """ 92*99e0aae7SDavid Rees __slots__ = ("canonical_name", "source_location", "visibility", "alias") 93*99e0aae7SDavid Rees 94*99e0aae7SDavid Rees # A LOCAL name is visible outside of its enclosing scope, but should not be 95*99e0aae7SDavid Rees # found when searching for a name. That is, this name should be matched in 96*99e0aae7SDavid Rees # the tail of a qualified reference (the 'bar' in 'foo.bar'), but not when 97*99e0aae7SDavid Rees # searching for names (the 'foo' in 'foo.bar' should not match outside of 98*99e0aae7SDavid Rees # 'foo's scope). This applies to public field names. 99*99e0aae7SDavid Rees LOCAL = object() 100*99e0aae7SDavid Rees 101*99e0aae7SDavid Rees # A PRIVATE name is similar to LOCAL except that it is never visible outside 102*99e0aae7SDavid Rees # its enclosing scope. This applies to abbreviations of field names: if 'a' 103*99e0aae7SDavid Rees # is an abbreviation for field 'apple', then 'foo.a' is not a valid reference; 104*99e0aae7SDavid Rees # instead it should be 'foo.apple'. 105*99e0aae7SDavid Rees PRIVATE = object() 106*99e0aae7SDavid Rees 107*99e0aae7SDavid Rees # A SEARCHABLE name is visible as long as it is in a scope in the search list. 108*99e0aae7SDavid Rees # This applies to type names ('Foo'), which may be found from many scopes. 109*99e0aae7SDavid Rees SEARCHABLE = object() 110*99e0aae7SDavid Rees 111*99e0aae7SDavid Rees def __init__(self, canonical_name, source_location, visibility, alias=None): 112*99e0aae7SDavid Rees super(_Scope, self).__init__() 113*99e0aae7SDavid Rees self.canonical_name = canonical_name 114*99e0aae7SDavid Rees self.source_location = source_location 115*99e0aae7SDavid Rees self.visibility = visibility 116*99e0aae7SDavid Rees self.alias = alias 117*99e0aae7SDavid Rees 118*99e0aae7SDavid Rees 119*99e0aae7SDavid Reesdef _add_name_to_scope(name_ir, scope, canonical_name, visibility, errors): 120*99e0aae7SDavid Rees """Adds the given name_ir to the given scope.""" 121*99e0aae7SDavid Rees name = name_ir.text 122*99e0aae7SDavid Rees new_scope = _Scope(canonical_name, name_ir.source_location, visibility) 123*99e0aae7SDavid Rees if name in scope: 124*99e0aae7SDavid Rees errors.append(duplicate_name_error( 125*99e0aae7SDavid Rees scope.canonical_name.module_file, name_ir.source_location, name, 126*99e0aae7SDavid Rees FileLocation(scope[name].canonical_name.module_file, 127*99e0aae7SDavid Rees scope[name].source_location))) 128*99e0aae7SDavid Rees else: 129*99e0aae7SDavid Rees scope[name] = new_scope 130*99e0aae7SDavid Rees return new_scope 131*99e0aae7SDavid Rees 132*99e0aae7SDavid Rees 133*99e0aae7SDavid Reesdef _add_name_to_scope_and_normalize(name_ir, scope, visibility, errors): 134*99e0aae7SDavid Rees """Adds the given name_ir to scope and sets its canonical_name.""" 135*99e0aae7SDavid Rees name = name_ir.name.text 136*99e0aae7SDavid Rees canonical_name = _nested_name(scope.canonical_name, name) 137*99e0aae7SDavid Rees ir_data_utils.builder(name_ir).canonical_name.CopyFrom(canonical_name) 138*99e0aae7SDavid Rees return _add_name_to_scope(name_ir.name, scope, canonical_name, visibility, 139*99e0aae7SDavid Rees errors) 140*99e0aae7SDavid Rees 141*99e0aae7SDavid Rees 142*99e0aae7SDavid Reesdef _add_struct_field_to_scope(field, scope, errors): 143*99e0aae7SDavid Rees """Adds the name of the given field to the scope.""" 144*99e0aae7SDavid Rees new_scope = _add_name_to_scope_and_normalize(field.name, scope, _Scope.LOCAL, 145*99e0aae7SDavid Rees errors) 146*99e0aae7SDavid Rees if field.HasField("abbreviation"): 147*99e0aae7SDavid Rees _add_name_to_scope(field.abbreviation, scope, new_scope.canonical_name, 148*99e0aae7SDavid Rees _Scope.PRIVATE, errors) 149*99e0aae7SDavid Rees 150*99e0aae7SDavid Rees value_builtin_name = ir_data.Word( 151*99e0aae7SDavid Rees text="this", 152*99e0aae7SDavid Rees source_location=ir_data.Location(is_synthetic=True), 153*99e0aae7SDavid Rees ) 154*99e0aae7SDavid Rees # In "inside field" scope, the name `this` maps back to the field itself. 155*99e0aae7SDavid Rees # This is important for attributes like `[requires]`. 156*99e0aae7SDavid Rees _add_name_to_scope(value_builtin_name, new_scope, 157*99e0aae7SDavid Rees field.name.canonical_name, _Scope.PRIVATE, errors) 158*99e0aae7SDavid Rees 159*99e0aae7SDavid Rees 160*99e0aae7SDavid Reesdef _add_parameter_name_to_scope(parameter, scope, errors): 161*99e0aae7SDavid Rees """Adds the name of the given parameter to the scope.""" 162*99e0aae7SDavid Rees _add_name_to_scope_and_normalize(parameter.name, scope, _Scope.LOCAL, errors) 163*99e0aae7SDavid Rees 164*99e0aae7SDavid Rees 165*99e0aae7SDavid Reesdef _add_enum_value_to_scope(value, scope, errors): 166*99e0aae7SDavid Rees """Adds the name of the enum value to scope.""" 167*99e0aae7SDavid Rees _add_name_to_scope_and_normalize(value.name, scope, _Scope.LOCAL, errors) 168*99e0aae7SDavid Rees 169*99e0aae7SDavid Rees 170*99e0aae7SDavid Reesdef _add_type_name_to_scope(type_definition, scope, errors): 171*99e0aae7SDavid Rees """Adds the name of type_definition to the given scope.""" 172*99e0aae7SDavid Rees new_scope = _add_name_to_scope_and_normalize(type_definition.name, scope, 173*99e0aae7SDavid Rees _Scope.SEARCHABLE, errors) 174*99e0aae7SDavid Rees return {"scope": new_scope} 175*99e0aae7SDavid Rees 176*99e0aae7SDavid Rees 177*99e0aae7SDavid Reesdef _set_scope_for_type_definition(type_definition, scope): 178*99e0aae7SDavid Rees """Sets the current scope for an ir_data.AddressableUnit.""" 179*99e0aae7SDavid Rees return {"scope": scope[type_definition.name.name.text]} 180*99e0aae7SDavid Rees 181*99e0aae7SDavid Rees 182*99e0aae7SDavid Reesdef _add_module_to_scope(module, scope): 183*99e0aae7SDavid Rees """Adds the name of the module to the given scope.""" 184*99e0aae7SDavid Rees module_symbol_table = _Scope( 185*99e0aae7SDavid Rees ir_data.CanonicalName(module_file=module.source_file_name, 186*99e0aae7SDavid Rees object_path=[]), 187*99e0aae7SDavid Rees None, 188*99e0aae7SDavid Rees _Scope.SEARCHABLE) 189*99e0aae7SDavid Rees scope[module.source_file_name] = module_symbol_table 190*99e0aae7SDavid Rees return {"scope": scope[module.source_file_name]} 191*99e0aae7SDavid Rees 192*99e0aae7SDavid Rees 193*99e0aae7SDavid Reesdef _set_scope_for_module(module, scope): 194*99e0aae7SDavid Rees """Adds the name of the module to the given scope.""" 195*99e0aae7SDavid Rees return {"scope": scope[module.source_file_name]} 196*99e0aae7SDavid Rees 197*99e0aae7SDavid Rees 198*99e0aae7SDavid Reesdef _add_import_to_scope(foreign_import, table, module, errors): 199*99e0aae7SDavid Rees if not foreign_import.local_name.text: 200*99e0aae7SDavid Rees # This is the prelude import; ignore it. 201*99e0aae7SDavid Rees return 202*99e0aae7SDavid Rees _add_alias_to_scope(foreign_import.local_name, table, module.canonical_name, 203*99e0aae7SDavid Rees [foreign_import.file_name.text], _Scope.SEARCHABLE, 204*99e0aae7SDavid Rees errors) 205*99e0aae7SDavid Rees 206*99e0aae7SDavid Rees 207*99e0aae7SDavid Reesdef _construct_symbol_tables(ir): 208*99e0aae7SDavid Rees """Constructs per-module symbol tables for each module in ir.""" 209*99e0aae7SDavid Rees symbol_tables = {} 210*99e0aae7SDavid Rees errors = [] 211*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 212*99e0aae7SDavid Rees ir, [ir_data.Module], _add_module_to_scope, 213*99e0aae7SDavid Rees parameters={"errors": errors, "scope": symbol_tables}) 214*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 215*99e0aae7SDavid Rees ir, [ir_data.TypeDefinition], _add_type_name_to_scope, 216*99e0aae7SDavid Rees incidental_actions={ir_data.Module: _set_scope_for_module}, 217*99e0aae7SDavid Rees parameters={"errors": errors, "scope": symbol_tables}) 218*99e0aae7SDavid Rees if errors: 219*99e0aae7SDavid Rees # Ideally, we would find duplicate field names elsewhere in the module, even 220*99e0aae7SDavid Rees # if there are duplicate type names, but field/enum names in the colliding 221*99e0aae7SDavid Rees # types also end up colliding, leading to spurious errors. E.g., if you 222*99e0aae7SDavid Rees # have two `struct Foo`s, then the field check will also discover a 223*99e0aae7SDavid Rees # collision for `$size_in_bytes`, since there are two `Foo.$size_in_bytes`. 224*99e0aae7SDavid Rees return symbol_tables, errors 225*99e0aae7SDavid Rees 226*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 227*99e0aae7SDavid Rees ir, [ir_data.EnumValue], _add_enum_value_to_scope, 228*99e0aae7SDavid Rees incidental_actions={ 229*99e0aae7SDavid Rees ir_data.Module: _set_scope_for_module, 230*99e0aae7SDavid Rees ir_data.TypeDefinition: _set_scope_for_type_definition, 231*99e0aae7SDavid Rees }, 232*99e0aae7SDavid Rees parameters={"errors": errors, "scope": symbol_tables}) 233*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 234*99e0aae7SDavid Rees ir, [ir_data.Field], _add_struct_field_to_scope, 235*99e0aae7SDavid Rees incidental_actions={ 236*99e0aae7SDavid Rees ir_data.Module: _set_scope_for_module, 237*99e0aae7SDavid Rees ir_data.TypeDefinition: _set_scope_for_type_definition, 238*99e0aae7SDavid Rees }, 239*99e0aae7SDavid Rees parameters={"errors": errors, "scope": symbol_tables}) 240*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 241*99e0aae7SDavid Rees ir, [ir_data.RuntimeParameter], _add_parameter_name_to_scope, 242*99e0aae7SDavid Rees incidental_actions={ 243*99e0aae7SDavid Rees ir_data.Module: _set_scope_for_module, 244*99e0aae7SDavid Rees ir_data.TypeDefinition: _set_scope_for_type_definition, 245*99e0aae7SDavid Rees }, 246*99e0aae7SDavid Rees parameters={"errors": errors, "scope": symbol_tables}) 247*99e0aae7SDavid Rees return symbol_tables, errors 248*99e0aae7SDavid Rees 249*99e0aae7SDavid Rees 250*99e0aae7SDavid Reesdef _add_alias_to_scope(name_ir, table, scope, alias, visibility, errors): 251*99e0aae7SDavid Rees """Adds the given name to the scope as an alias.""" 252*99e0aae7SDavid Rees name = name_ir.text 253*99e0aae7SDavid Rees new_scope = _Scope(_nested_name(scope, name), name_ir.source_location, 254*99e0aae7SDavid Rees visibility, alias) 255*99e0aae7SDavid Rees scoped_table = table[scope.module_file] 256*99e0aae7SDavid Rees for path_element in scope.object_path: 257*99e0aae7SDavid Rees scoped_table = scoped_table[path_element] 258*99e0aae7SDavid Rees if name in scoped_table: 259*99e0aae7SDavid Rees errors.append(duplicate_name_error( 260*99e0aae7SDavid Rees scoped_table.canonical_name.module_file, name_ir.source_location, name, 261*99e0aae7SDavid Rees FileLocation(scoped_table[name].canonical_name.module_file, 262*99e0aae7SDavid Rees scoped_table[name].source_location))) 263*99e0aae7SDavid Rees else: 264*99e0aae7SDavid Rees scoped_table[name] = new_scope 265*99e0aae7SDavid Rees return new_scope 266*99e0aae7SDavid Rees 267*99e0aae7SDavid Rees 268*99e0aae7SDavid Reesdef _resolve_head_of_field_reference(field_reference, table, current_scope, 269*99e0aae7SDavid Rees visible_scopes, source_file_name, errors): 270*99e0aae7SDavid Rees return _resolve_reference( 271*99e0aae7SDavid Rees field_reference.path[0], table, current_scope, 272*99e0aae7SDavid Rees visible_scopes, source_file_name, errors) 273*99e0aae7SDavid Rees 274*99e0aae7SDavid Rees 275*99e0aae7SDavid Reesdef _resolve_reference(reference, table, current_scope, visible_scopes, 276*99e0aae7SDavid Rees source_file_name, errors): 277*99e0aae7SDavid Rees """Sets the canonical name of the given reference.""" 278*99e0aae7SDavid Rees if reference.HasField("canonical_name"): 279*99e0aae7SDavid Rees # This reference has already been resolved by the _resolve_field_reference 280*99e0aae7SDavid Rees # pass. 281*99e0aae7SDavid Rees return 282*99e0aae7SDavid Rees target = _find_target_of_reference(reference, table, current_scope, 283*99e0aae7SDavid Rees visible_scopes, source_file_name, errors) 284*99e0aae7SDavid Rees if target is not None: 285*99e0aae7SDavid Rees assert not target.alias 286*99e0aae7SDavid Rees ir_data_utils.builder(reference).canonical_name.CopyFrom(target.canonical_name) 287*99e0aae7SDavid Rees 288*99e0aae7SDavid Rees 289*99e0aae7SDavid Reesdef _find_target_of_reference(reference, table, current_scope, visible_scopes, 290*99e0aae7SDavid Rees source_file_name, errors): 291*99e0aae7SDavid Rees """Returns the resolved name of the given reference.""" 292*99e0aae7SDavid Rees found_in_table = None 293*99e0aae7SDavid Rees name = reference.source_name[0].text 294*99e0aae7SDavid Rees for scope in visible_scopes: 295*99e0aae7SDavid Rees scoped_table = table[scope.module_file] 296*99e0aae7SDavid Rees for path_element in scope.object_path or []: 297*99e0aae7SDavid Rees scoped_table = scoped_table[path_element] 298*99e0aae7SDavid Rees if (name in scoped_table and 299*99e0aae7SDavid Rees (scope == current_scope or 300*99e0aae7SDavid Rees scoped_table[name].visibility == _Scope.SEARCHABLE)): 301*99e0aae7SDavid Rees # Prelude is "", so explicitly check for None. 302*99e0aae7SDavid Rees if found_in_table is not None: 303*99e0aae7SDavid Rees # TODO(bolms): Currently, this catches the case where a module tries to 304*99e0aae7SDavid Rees # use a name that is defined (at the same scope) in two different 305*99e0aae7SDavid Rees # modules. It may make sense to raise duplicate_name_error whenever two 306*99e0aae7SDavid Rees # modules define the same name (whether it is used or not), and reserve 307*99e0aae7SDavid Rees # ambiguous_name_error for cases where a name is found in multiple 308*99e0aae7SDavid Rees # scopes. 309*99e0aae7SDavid Rees errors.append(ambiguous_name_error( 310*99e0aae7SDavid Rees source_file_name, reference.source_location, name, [FileLocation( 311*99e0aae7SDavid Rees found_in_table[name].canonical_name.module_file, 312*99e0aae7SDavid Rees found_in_table[name].source_location), FileLocation( 313*99e0aae7SDavid Rees scoped_table[name].canonical_name.module_file, scoped_table[ 314*99e0aae7SDavid Rees name].source_location)])) 315*99e0aae7SDavid Rees continue 316*99e0aae7SDavid Rees found_in_table = scoped_table 317*99e0aae7SDavid Rees if reference.is_local_name: 318*99e0aae7SDavid Rees # This is a little hacky. When "is_local_name" is True, the name refers 319*99e0aae7SDavid Rees # to a type that was defined inline. In many cases, the type should be 320*99e0aae7SDavid Rees # found at the same scope as the field; e.g.: 321*99e0aae7SDavid Rees # 322*99e0aae7SDavid Rees # struct Foo: 323*99e0aae7SDavid Rees # 0 [+1] enum bar: 324*99e0aae7SDavid Rees # BAZ = 1 325*99e0aae7SDavid Rees # 326*99e0aae7SDavid Rees # In this case, `Foo.bar` has type `Foo.Bar`. Unfortunately, things 327*99e0aae7SDavid Rees # break down a little bit when there is an inline type in an anonymous 328*99e0aae7SDavid Rees # `bits`: 329*99e0aae7SDavid Rees # 330*99e0aae7SDavid Rees # struct Foo: 331*99e0aae7SDavid Rees # 0 [+1] bits: 332*99e0aae7SDavid Rees # 0 [+7] enum bar: 333*99e0aae7SDavid Rees # BAZ = 1 334*99e0aae7SDavid Rees # 335*99e0aae7SDavid Rees # Types inside of anonymous `bits` are hoisted into their parent type, 336*99e0aae7SDavid Rees # so instead of `Foo.EmbossReservedAnonymous1.Bar`, `bar`'s type is just 337*99e0aae7SDavid Rees # `Foo.Bar`. Unfortunately, the field is still 338*99e0aae7SDavid Rees # `Foo.EmbossReservedAnonymous1.bar`, so `bar`'s type won't be found in 339*99e0aae7SDavid Rees # `bar`'s `current_scope`. 340*99e0aae7SDavid Rees # 341*99e0aae7SDavid Rees # (The name `bar` is exposed from `Foo` as an alias virtual field, so 342*99e0aae7SDavid Rees # perhaps the correct answer is to allow type aliases, so that `Bar` can 343*99e0aae7SDavid Rees # be found in both `Foo` and `Foo.EmbossReservedAnonymous1`. That would 344*99e0aae7SDavid Rees # involve an entirely new feature, though.) 345*99e0aae7SDavid Rees # 346*99e0aae7SDavid Rees # The workaround here is to search scopes from the innermost outward, 347*99e0aae7SDavid Rees # and just stop as soon as a match is found. This isn't ideal, because 348*99e0aae7SDavid Rees # it relies on other bits of the front end having correctly added the 349*99e0aae7SDavid Rees # inline type to the correct scope before symbol resolution, but it does 350*99e0aae7SDavid Rees # work. Names with False `is_local_name` will still be checked for 351*99e0aae7SDavid Rees # ambiguity. 352*99e0aae7SDavid Rees break 353*99e0aae7SDavid Rees if found_in_table is None: 354*99e0aae7SDavid Rees errors.append(missing_name_error( 355*99e0aae7SDavid Rees source_file_name, reference.source_name[0].source_location, name)) 356*99e0aae7SDavid Rees if not errors: 357*99e0aae7SDavid Rees for subname in reference.source_name: 358*99e0aae7SDavid Rees if subname.text not in found_in_table: 359*99e0aae7SDavid Rees errors.append(missing_name_error(source_file_name, 360*99e0aae7SDavid Rees subname.source_location, subname.text)) 361*99e0aae7SDavid Rees return None 362*99e0aae7SDavid Rees found_in_table = found_in_table[subname.text] 363*99e0aae7SDavid Rees while found_in_table.alias: 364*99e0aae7SDavid Rees referenced_table = table 365*99e0aae7SDavid Rees for name in found_in_table.alias: 366*99e0aae7SDavid Rees referenced_table = referenced_table[name] 367*99e0aae7SDavid Rees # TODO(bolms): This section should really be a recursive lookup 368*99e0aae7SDavid Rees # function, which would be able to handle arbitrary aliases through 369*99e0aae7SDavid Rees # other aliases. 370*99e0aae7SDavid Rees # 371*99e0aae7SDavid Rees # This should be fine for now, since the only aliases here should be 372*99e0aae7SDavid Rees # imports, which can't refer to other imports. 373*99e0aae7SDavid Rees assert not referenced_table.alias, "Alias found to contain alias." 374*99e0aae7SDavid Rees found_in_table = referenced_table 375*99e0aae7SDavid Rees return found_in_table 376*99e0aae7SDavid Rees return None 377*99e0aae7SDavid Rees 378*99e0aae7SDavid Rees 379*99e0aae7SDavid Reesdef _resolve_field_reference(field_reference, source_file_name, errors, ir): 380*99e0aae7SDavid Rees """Resolves the References inside of a FieldReference.""" 381*99e0aae7SDavid Rees if field_reference.path[-1].HasField("canonical_name"): 382*99e0aae7SDavid Rees # Already done. 383*99e0aae7SDavid Rees return 384*99e0aae7SDavid Rees previous_field = ir_util.find_object_or_none(field_reference.path[0], ir) 385*99e0aae7SDavid Rees previous_reference = field_reference.path[0] 386*99e0aae7SDavid Rees for ref in field_reference.path[1:]: 387*99e0aae7SDavid Rees while ir_util.field_is_virtual(previous_field): 388*99e0aae7SDavid Rees if (previous_field.read_transform.WhichOneof("expression") == 389*99e0aae7SDavid Rees "field_reference"): 390*99e0aae7SDavid Rees # Pass a separate error list into the recursive _resolve_field_reference 391*99e0aae7SDavid Rees # call so that only one copy of the error for a particular reference 392*99e0aae7SDavid Rees # will actually surface: in particular, the one that results from a 393*99e0aae7SDavid Rees # direct call from traverse_ir_top_down into _resolve_field_reference. 394*99e0aae7SDavid Rees new_errors = [] 395*99e0aae7SDavid Rees _resolve_field_reference( 396*99e0aae7SDavid Rees previous_field.read_transform.field_reference, 397*99e0aae7SDavid Rees previous_field.name.canonical_name.module_file, new_errors, ir) 398*99e0aae7SDavid Rees # If the recursive _resolve_field_reference was unable to resolve the 399*99e0aae7SDavid Rees # field, then bail. Otherwise we get a cascade of errors, where an 400*99e0aae7SDavid Rees # error in `x` leads to errors in anything trying to reach a member of 401*99e0aae7SDavid Rees # `x`. 402*99e0aae7SDavid Rees if not previous_field.read_transform.field_reference.path[-1].HasField( 403*99e0aae7SDavid Rees "canonical_name"): 404*99e0aae7SDavid Rees return 405*99e0aae7SDavid Rees previous_field = ir_util.find_object( 406*99e0aae7SDavid Rees previous_field.read_transform.field_reference.path[-1], ir) 407*99e0aae7SDavid Rees else: 408*99e0aae7SDavid Rees errors.append( 409*99e0aae7SDavid Rees noncomposite_subfield_error(source_file_name, 410*99e0aae7SDavid Rees previous_reference.source_location, 411*99e0aae7SDavid Rees previous_reference.source_name[0].text)) 412*99e0aae7SDavid Rees return 413*99e0aae7SDavid Rees if previous_field.type.WhichOneof("type") == "array_type": 414*99e0aae7SDavid Rees errors.append( 415*99e0aae7SDavid Rees array_subfield_error(source_file_name, 416*99e0aae7SDavid Rees previous_reference.source_location, 417*99e0aae7SDavid Rees previous_reference.source_name[0].text)) 418*99e0aae7SDavid Rees return 419*99e0aae7SDavid Rees assert previous_field.type.WhichOneof("type") == "atomic_type" 420*99e0aae7SDavid Rees member_name = ir_data_utils.copy( 421*99e0aae7SDavid Rees previous_field.type.atomic_type.reference.canonical_name) 422*99e0aae7SDavid Rees ir_data_utils.builder(member_name).object_path.extend([ref.source_name[0].text]) 423*99e0aae7SDavid Rees previous_field = ir_util.find_object_or_none(member_name, ir) 424*99e0aae7SDavid Rees if previous_field is None: 425*99e0aae7SDavid Rees errors.append( 426*99e0aae7SDavid Rees missing_name_error(source_file_name, 427*99e0aae7SDavid Rees ref.source_name[0].source_location, 428*99e0aae7SDavid Rees ref.source_name[0].text)) 429*99e0aae7SDavid Rees return 430*99e0aae7SDavid Rees ir_data_utils.builder(ref).canonical_name.CopyFrom(member_name) 431*99e0aae7SDavid Rees previous_reference = ref 432*99e0aae7SDavid Rees 433*99e0aae7SDavid Rees 434*99e0aae7SDavid Reesdef _set_visible_scopes_for_type_definition(type_definition, visible_scopes): 435*99e0aae7SDavid Rees """Sets current_scope and visible_scopes for the given type_definition.""" 436*99e0aae7SDavid Rees return { 437*99e0aae7SDavid Rees "current_scope": type_definition.name.canonical_name, 438*99e0aae7SDavid Rees 439*99e0aae7SDavid Rees # In order to ensure that the iteration through scopes in 440*99e0aae7SDavid Rees # _find_target_of_reference will go from innermost to outermost, it is 441*99e0aae7SDavid Rees # important that the current scope (type_definition.name.canonical_name) 442*99e0aae7SDavid Rees # precedes the previous visible_scopes here. 443*99e0aae7SDavid Rees "visible_scopes": (type_definition.name.canonical_name,) + visible_scopes, 444*99e0aae7SDavid Rees } 445*99e0aae7SDavid Rees 446*99e0aae7SDavid Rees 447*99e0aae7SDavid Reesdef _set_visible_scopes_for_module(module): 448*99e0aae7SDavid Rees """Sets visible_scopes for the given module.""" 449*99e0aae7SDavid Rees self_scope = ir_data.CanonicalName(module_file=module.source_file_name) 450*99e0aae7SDavid Rees extra_visible_scopes = [] 451*99e0aae7SDavid Rees for foreign_import in module.foreign_import: 452*99e0aae7SDavid Rees # Anonymous imports are searched for top-level names; named imports are not. 453*99e0aae7SDavid Rees # As of right now, only the prelude should be imported anonymously; other 454*99e0aae7SDavid Rees # modules must be imported with names. 455*99e0aae7SDavid Rees if not foreign_import.local_name.text: 456*99e0aae7SDavid Rees extra_visible_scopes.append( 457*99e0aae7SDavid Rees ir_data.CanonicalName(module_file=foreign_import.file_name.text)) 458*99e0aae7SDavid Rees return {"visible_scopes": (self_scope,) + tuple(extra_visible_scopes)} 459*99e0aae7SDavid Rees 460*99e0aae7SDavid Rees 461*99e0aae7SDavid Reesdef _set_visible_scopes_for_attribute(attribute, field, visible_scopes): 462*99e0aae7SDavid Rees """Sets current_scope and visible_scopes for the attribute.""" 463*99e0aae7SDavid Rees del attribute # Unused 464*99e0aae7SDavid Rees if field is None: 465*99e0aae7SDavid Rees return 466*99e0aae7SDavid Rees return { 467*99e0aae7SDavid Rees "current_scope": field.name.canonical_name, 468*99e0aae7SDavid Rees "visible_scopes": (field.name.canonical_name,) + visible_scopes, 469*99e0aae7SDavid Rees } 470*99e0aae7SDavid Rees 471*99e0aae7SDavid Reesdef _module_source_from_table_action(m, table): 472*99e0aae7SDavid Rees return {"module": table[m.source_file_name]} 473*99e0aae7SDavid Rees 474*99e0aae7SDavid Reesdef _resolve_symbols_from_table(ir, table): 475*99e0aae7SDavid Rees """Resolves all references in the given IR, given the constructed table.""" 476*99e0aae7SDavid Rees errors = [] 477*99e0aae7SDavid Rees # Symbol resolution is broken into five passes. First, this code resolves any 478*99e0aae7SDavid Rees # imports, and adds import aliases to modules. 479*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 480*99e0aae7SDavid Rees ir, [ir_data.Import], _add_import_to_scope, 481*99e0aae7SDavid Rees incidental_actions={ 482*99e0aae7SDavid Rees ir_data.Module: _module_source_from_table_action, 483*99e0aae7SDavid Rees }, 484*99e0aae7SDavid Rees parameters={"errors": errors, "table": table}) 485*99e0aae7SDavid Rees if errors: 486*99e0aae7SDavid Rees return errors 487*99e0aae7SDavid Rees # Next, this resolves all absolute references (e.g., it resolves "UInt" in 488*99e0aae7SDavid Rees # "0:1 UInt field" to [prelude]::UInt). 489*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 490*99e0aae7SDavid Rees ir, [ir_data.Reference], _resolve_reference, 491*99e0aae7SDavid Rees skip_descendants_of=(ir_data.FieldReference,), 492*99e0aae7SDavid Rees incidental_actions={ 493*99e0aae7SDavid Rees ir_data.TypeDefinition: _set_visible_scopes_for_type_definition, 494*99e0aae7SDavid Rees ir_data.Module: _set_visible_scopes_for_module, 495*99e0aae7SDavid Rees ir_data.Attribute: _set_visible_scopes_for_attribute, 496*99e0aae7SDavid Rees }, 497*99e0aae7SDavid Rees parameters={"table": table, "errors": errors, "field": None}) 498*99e0aae7SDavid Rees # Lastly, head References to fields (e.g., the `a` of `a.b.c`) are resolved. 499*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 500*99e0aae7SDavid Rees ir, [ir_data.FieldReference], _resolve_head_of_field_reference, 501*99e0aae7SDavid Rees incidental_actions={ 502*99e0aae7SDavid Rees ir_data.TypeDefinition: _set_visible_scopes_for_type_definition, 503*99e0aae7SDavid Rees ir_data.Module: _set_visible_scopes_for_module, 504*99e0aae7SDavid Rees ir_data.Attribute: _set_visible_scopes_for_attribute, 505*99e0aae7SDavid Rees }, 506*99e0aae7SDavid Rees parameters={"table": table, "errors": errors, "field": None}) 507*99e0aae7SDavid Rees return errors 508*99e0aae7SDavid Rees 509*99e0aae7SDavid Rees 510*99e0aae7SDavid Reesdef resolve_field_references(ir): 511*99e0aae7SDavid Rees """Resolves structure member accesses ("field.subfield") in ir.""" 512*99e0aae7SDavid Rees errors = [] 513*99e0aae7SDavid Rees traverse_ir.fast_traverse_ir_top_down( 514*99e0aae7SDavid Rees ir, [ir_data.FieldReference], _resolve_field_reference, 515*99e0aae7SDavid Rees incidental_actions={ 516*99e0aae7SDavid Rees ir_data.TypeDefinition: _set_visible_scopes_for_type_definition, 517*99e0aae7SDavid Rees ir_data.Module: _set_visible_scopes_for_module, 518*99e0aae7SDavid Rees ir_data.Attribute: _set_visible_scopes_for_attribute, 519*99e0aae7SDavid Rees }, 520*99e0aae7SDavid Rees parameters={"errors": errors, "field": None}) 521*99e0aae7SDavid Rees return errors 522*99e0aae7SDavid Rees 523*99e0aae7SDavid Rees 524*99e0aae7SDavid Reesdef resolve_symbols(ir): 525*99e0aae7SDavid Rees """Resolves the symbols in all modules in ir.""" 526*99e0aae7SDavid Rees symbol_tables, errors = _construct_symbol_tables(ir) 527*99e0aae7SDavid Rees if errors: 528*99e0aae7SDavid Rees return errors 529*99e0aae7SDavid Rees return _resolve_symbols_from_table(ir, symbol_tables) 530