1 module d.ir.dscope; 2 3 import d.ir.symbol; 4 5 import d.ast.conditional; 6 7 import source.location; 8 import source.name; 9 10 struct ConditionalBranch { 11 // XXX: Can't be private because mixin templates are a glorious hack. 12 // private: 13 // XXX: Because mixin if such a fucking broken way to go: 14 static import d.ast.declaration; 15 alias Declaration = d.ast.declaration.Declaration; 16 17 import std.bitmanip; 18 mixin(taggedClassRef!( 19 StaticIfDeclaration, "sif", 20 bool, "branch", 1, 21 )); 22 23 public: 24 this(StaticIfDeclaration sif, bool branch) { 25 // XXX: Need to set the branch first because of 26 // https://issues.dlang.org/show_bug.cgi?id=15305 27 this.branch = branch; 28 this.sif = sif; 29 } 30 } 31 32 /** 33 * Tools to make symbols Scopes. 34 */ 35 interface Scope { 36 Module getModule(); 37 Scope getParentScope(); 38 39 Module[] getImports(); 40 void addImport(Module m); 41 42 Symbol search(Location location, Name name); 43 Symbol resolve(Location location, Name name); 44 45 void addSymbol(Symbol s); 46 void addOverloadableSymbol(Symbol s); 47 void addConditionalSymbol(Symbol s, ConditionalBranch[] cdBranches); 48 49 void setPoisoningMode(); 50 void clearPoisoningMode(); 51 } 52 53 enum ScopeType { 54 Module, 55 WithParent, 56 Nested, 57 } 58 59 mixin template ScopeImpl( 60 ScopeType ST = ScopeType.WithParent, 61 ParentScope = Scope, 62 ) { 63 private: 64 import d.ir.symbol; 65 Module dmodule; 66 static if (ST) { 67 ParentScope parentScope; 68 } 69 70 import source.name; 71 Symbol[Name] symbols; 72 73 static if (ST) { 74 // XXX: Use a proper set :D 75 bool[Variable] captures; 76 } 77 78 Module[] imports; 79 80 bool isPoisoning; 81 bool isPoisoned; 82 bool hasConditional; 83 84 final: 85 static if (ST) { 86 void fillParentScope(ParentScope parentScope) { 87 this.dmodule = parentScope.getModule(); 88 this.parentScope = parentScope; 89 } 90 } 91 92 public: 93 Module getModule() { 94 assert(dmodule !is null, "No module"); 95 return dmodule; 96 } 97 98 ParentScope getParentScope() { 99 static if (ST) { 100 assert(parentScope !is null); 101 return parentScope; 102 } else { 103 return null; 104 } 105 } 106 107 static if (ST) { 108 bool[Variable] getCaptures() { 109 return captures; 110 } 111 } 112 113 Module[] getImports() { 114 return imports; 115 } 116 117 void addImport(Module m) { 118 imports ~= m; 119 } 120 121 Symbol search(Location location, Name name) { 122 if (auto s = resolve(location, name)) { 123 return s; 124 } 125 126 Symbol s = null; 127 static if (ST) { 128 s = parentScope.search(location, name); 129 } 130 131 static if (!is(typeof(hasContext))) { 132 enum hasContext = false; 133 } 134 135 if (s is null || !hasContext) { 136 return s; 137 } 138 139 static if (ST) { 140 if (auto v = cast(Variable) s) { 141 import d.common.qualifier; 142 if (v.storage.isLocal) { 143 captures[v] = true; 144 v.storage = Storage.Capture; 145 } 146 } 147 } 148 149 return s; 150 } 151 152 Symbol resolve(Location location, Name name) { 153 auto sPtr = name in symbols; 154 if (sPtr is null) { 155 if (isPoisoning) { 156 symbols[name] = new Poison(location, name); 157 isPoisoned = true; 158 } 159 160 return null; 161 } 162 163 auto s = *sPtr; 164 165 static if (ST == ScopeType.Nested) { 166 // For nested scope we unpack and/or mark overloadset as already 167 // resolved so we make copy of it when adding new overloads. 168 if (auto os = cast(OverloadSet) s) { 169 os.isPoisoned = isPoisoning; 170 if (os.set.length == 1) { 171 return os.set[0]; 172 } 173 174 os.isResolved = true; 175 return os; 176 } 177 } 178 179 // If we are poisoning, we need to make sure we poison. 180 // If not, we can return directly. 181 if (!isPoisoning) { 182 return s; 183 } 184 185 static if (ST != ScopeType.Nested) { 186 // If we have an overloadset, make it poisoned. 187 if (auto os = cast(OverloadSet) s) { 188 os.isPoisoned = true; 189 return s; 190 } 191 } 192 193 // If we have a poison, then pretend there is nothing. 194 if (cast(Poison) s) { 195 return null; 196 } 197 198 // If we have no conditionals, no need to check for them. 199 if (!hasConditional) { 200 return s; 201 } 202 203 // If we have a conditional, poison it. 204 if (auto cs = cast(ConditionalSet) s) { 205 cs.isPoisoned = true; 206 return cs.selected; 207 } 208 209 return s; 210 } 211 212 void addSymbol(Symbol s) { 213 assert( 214 !s.name.isEmpty, 215 "Symbol can't be added to scope as it has no name." 216 ); 217 218 if (auto sPtr = s.name in symbols) { 219 if (auto p = cast(Poison) *sPtr) { 220 import source.exception; 221 throw new CompileException(s.location, "Poisoned"); 222 } 223 224 import source.exception; 225 throw new CompileException(s.location, "Already defined"); 226 } 227 228 symbols[s.name] = s; 229 } 230 231 void addOverloadableSymbol(Symbol s) { 232 if (auto sPtr = s.name in symbols) { 233 if (auto os = cast(OverloadSet) *sPtr) { 234 if (os.isPoisoned) { 235 import source.exception; 236 throw new CompileException(s.location, "Poisoned"); 237 } 238 239 if (ST == ScopeType.Nested && os.isResolved) { 240 *sPtr = os = os.clone(); 241 } 242 243 os.set ~= s; 244 return; 245 } 246 } 247 248 addSymbol(new OverloadSet(s.location, s.name, [s])); 249 } 250 251 void addConditionalSymbol(Symbol s, ConditionalBranch[] cdBranches) in { 252 assert(cdBranches.length > 0, "No conditional branches supplied"); 253 } do { 254 auto entry = ConditionalEntry(s, cdBranches); 255 if (auto csPtr = s.name in symbols) { 256 if (auto cs = cast(ConditionalSet) *csPtr) { 257 cs.set ~= entry; 258 return; 259 } 260 261 import source.exception; 262 throw new CompileException(s.location, "Already defined"); 263 } 264 265 symbols[s.name] = new ConditionalSet(s.location, s.name, [entry]); 266 hasConditional = true; 267 } 268 269 void setPoisoningMode() in { 270 assert(isPoisoning == false, "poisoning mode is already on."); 271 } do { 272 isPoisoning = true; 273 } 274 275 void clearPoisoningMode() in { 276 assert(isPoisoning == true, "poisoning mode is not on."); 277 } do { 278 // XXX: Consider not removing tags on OverloadSet. 279 // That would allow to not pass over the AA most of the time. 280 foreach(n; symbols.keys) { 281 auto s = symbols[n]; 282 283 // Mark overload set as non poisoned. 284 // XXX: Why ?!?? 285 if (auto os = cast(OverloadSet) s) { 286 os.isPoisoned = false; 287 continue; 288 } 289 290 // Remove poisons. 291 // XXX: Why ?!!??! 292 if (isPoisoned && cast(Poison) s) { 293 symbols.remove(n); 294 continue; 295 } 296 297 // If we have no conditionals, no need to check for them. 298 if (!hasConditional) { 299 continue; 300 } 301 302 // Replace conditional entrie by whatever they resolve to. 303 if (auto cs = cast(ConditionalSet) s) { 304 if (cs.set.length) { 305 import source.exception; 306 throw new CompileException( 307 cs.set[0].entry.location, 308 "Not resolved", 309 ); 310 } 311 312 assert( 313 cs.set.length == 0, 314 "Conditional symbols remains when clearing poisoning mode." 315 ); 316 if (cs.selected) { 317 symbols[n] = cs.selected; 318 } else { 319 symbols.remove(n); 320 } 321 } 322 } 323 324 isPoisoning = false; 325 isPoisoned = false; 326 hasConditional = false; 327 } 328 329 // XXX: Use of smarter data structure can probably improve things here :D 330 import d.ast.conditional : StaticIfDeclaration; 331 void resolveConditional(StaticIfDeclaration sif, bool branch) in { 332 assert( 333 isPoisoning, 334 "You must be in poisoning mode when resolving static ifs." 335 ); 336 } do { 337 foreach(s; symbols.values) { 338 if (auto cs = cast(ConditionalSet) s) { 339 ConditionalEntry[] newSet; 340 foreach(ce; cs.set) { 341 // This is not the symbol we are interested in, move on. 342 if (ce.cdBranches[0].sif !is sif) { 343 newSet ~= ce; 344 continue; 345 } 346 347 // If this is not the right branch, forget. 348 if (ce.cdBranches[0].branch != branch) { 349 continue; 350 } 351 352 // The top level static if is resolved, drop. 353 ce.cdBranches = ce.cdBranches[1 .. $]; 354 355 // There are nested static ifs, put back in the set. 356 if (ce.cdBranches.length) { 357 newSet ~= ce; 358 continue; 359 } 360 361 // FIXME: Check if it is an overloadable symbol. 362 assert(cs.selected is null, "overload ? bug ?"); 363 364 // We have a new symbol, select it. 365 if (cs.isPoisoned) { 366 import source.exception; 367 throw new CompileException(s.location, "Poisoned"); 368 } 369 370 cs.selected = ce.entry; 371 } 372 373 cs.set = newSet; 374 } 375 } 376 } 377 } 378 379 final: 380 381 /** 382 * A scope associate identifier with declarations. 383 */ 384 class NestedScope : Scope { 385 // TODO: Remove the module field, which can be access from fun. 386 Function fun; 387 388 mixin ScopeImpl!(ScopeType.Nested); 389 390 this(Function fun) { 391 this.fun = fun; 392 this.parentScope = fun; 393 } 394 395 this(NestedScope parentScope) { 396 this.fun = parentScope.fun; 397 this.parentScope = parentScope; 398 } 399 400 this(Scope s) { 401 if (auto n = cast(NestedScope) s) { 402 this(n); 403 } else if (auto f = cast(Function) s) { 404 this(f); 405 } else { 406 assert(0, "Parent scope must be a function or a nested scope"); 407 } 408 } 409 410 Module getModule() { 411 return fun.getModule(); 412 } 413 } 414 415 // XXX: Can't be private because mixin templates are a glorious hack. 416 // private: 417 class Poison : Symbol { 418 this(Location location, Name name) { 419 super(location, name); 420 } 421 } 422 423 struct ConditionalEntry { 424 Symbol entry; 425 ConditionalBranch[] cdBranches; 426 } 427 428 class ConditionalSet : Symbol { 429 ConditionalEntry[] set; 430 431 Symbol selected; 432 433 this(Location location, Name name, ConditionalEntry[] set) { 434 super(location, name); 435 this.set = set; 436 } 437 }