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 // sdfmt off 20 StaticIfDeclaration, "sif", 21 bool, "branch", 1, 22 // sdfmt on 23 )); 24 25 public: 26 this(StaticIfDeclaration sif, bool branch) { 27 // XXX: Need to set the branch first because of 28 // https://issues.dlang.org/show_bug.cgi?id=15305 29 this.branch = branch; 30 this.sif = sif; 31 } 32 } 33 34 /** 35 * Tools to make symbols Scopes. 36 */ 37 interface Scope { 38 Module getModule(); 39 Scope getParentScope(); 40 41 Module[] getImports(); 42 void addImport(Module m); 43 44 Symbol search(Location location, Name name); 45 Symbol resolve(Location location, Name name); 46 47 void addSymbol(Symbol s); 48 void addOverloadableSymbol(Symbol s); 49 void addConditionalSymbol(Symbol s, ConditionalBranch[] cdBranches); 50 51 void setPoisoningMode(); 52 void clearPoisoningMode(); 53 } 54 55 enum ScopeType { 56 Module, 57 WithParent, 58 Nested, 59 } 60 61 mixin template ScopeImpl(ScopeType ST = ScopeType.WithParent, 62 ParentScope = Scope) { 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(!s.name.isEmpty, 214 "Symbol can't be added to scope as it has no name."); 215 216 if (auto sPtr = s.name in symbols) { 217 if (auto p = cast(Poison) *sPtr) { 218 import source.exception; 219 throw new CompileException(s.location, "Poisoned"); 220 } 221 222 import source.exception; 223 throw new CompileException(s.location, "Already defined"); 224 } 225 226 symbols[s.name] = s; 227 } 228 229 void addOverloadableSymbol(Symbol s) { 230 if (auto sPtr = s.name in symbols) { 231 if (auto os = cast(OverloadSet) *sPtr) { 232 if (os.isPoisoned) { 233 import source.exception; 234 throw new CompileException(s.location, "Poisoned"); 235 } 236 237 if (ST == ScopeType.Nested && os.isResolved) { 238 *sPtr = os = os.clone(); 239 } 240 241 os.set ~= s; 242 return; 243 } 244 } 245 246 addSymbol(new OverloadSet(s.location, s.name, [s])); 247 } 248 249 void addConditionalSymbol(Symbol s, ConditionalBranch[] cdBranches) in { 250 assert(cdBranches.length > 0, "No conditional branches supplied"); 251 } do { 252 auto entry = ConditionalEntry(s, cdBranches); 253 if (auto csPtr = s.name in symbols) { 254 if (auto cs = cast(ConditionalSet) *csPtr) { 255 cs.set ~= entry; 256 return; 257 } 258 259 import source.exception; 260 throw new CompileException(s.location, "Already defined"); 261 } 262 263 symbols[s.name] = new ConditionalSet(s.location, s.name, [entry]); 264 hasConditional = true; 265 } 266 267 void setPoisoningMode() in { 268 assert(isPoisoning == false, "poisoning mode is already on."); 269 } do { 270 isPoisoning = true; 271 } 272 273 void clearPoisoningMode() in { 274 assert(isPoisoning == true, "poisoning mode is not on."); 275 } do { 276 // XXX: Consider not removing tags on OverloadSet. 277 // That would allow to not pass over the AA most of the time. 278 foreach (n; symbols.keys) { 279 auto s = symbols[n]; 280 281 // Mark overload set as non poisoned. 282 // XXX: Why ?!?? 283 if (auto os = cast(OverloadSet) s) { 284 os.isPoisoned = false; 285 continue; 286 } 287 288 // Remove poisons. 289 // XXX: Why ?!!??! 290 if (isPoisoned && cast(Poison) s) { 291 symbols.remove(n); 292 continue; 293 } 294 295 // If we have no conditionals, no need to check for them. 296 if (!hasConditional) { 297 continue; 298 } 299 300 // Replace conditional entrie by whatever they resolve to. 301 if (auto cs = cast(ConditionalSet) s) { 302 if (cs.set.length) { 303 import source.exception; 304 throw new CompileException(cs.set[0].entry.location, 305 "Not resolved"); 306 } 307 308 assert( 309 cs.set.length == 0, 310 "Conditional symbols remains when clearing poisoning mode."); 311 if (cs.selected) { 312 symbols[n] = cs.selected; 313 } else { 314 symbols.remove(n); 315 } 316 } 317 } 318 319 isPoisoning = false; 320 isPoisoned = false; 321 hasConditional = false; 322 } 323 324 // XXX: Use of smarter data structure can probably improve things here :D 325 import d.ast.conditional : StaticIfDeclaration; 326 void resolveConditional(StaticIfDeclaration sif, bool branch) in { 327 assert(isPoisoning, 328 "You must be in poisoning mode when resolving static ifs."); 329 } do { 330 foreach (s; symbols.values) { 331 if (auto cs = cast(ConditionalSet) s) { 332 ConditionalEntry[] newSet; 333 foreach (ce; cs.set) { 334 // This is not the symbol we are interested in, move on. 335 if (ce.cdBranches[0].sif !is sif) { 336 newSet ~= ce; 337 continue; 338 } 339 340 // If this is not the right branch, forget. 341 if (ce.cdBranches[0].branch != branch) { 342 continue; 343 } 344 345 // The top level static if is resolved, drop. 346 ce.cdBranches = ce.cdBranches[1 .. $]; 347 348 // There are nested static ifs, put back in the set. 349 if (ce.cdBranches.length) { 350 newSet ~= ce; 351 continue; 352 } 353 354 // FIXME: Check if it is an overloadable symbol. 355 assert(cs.selected is null, "overload ? bug ?"); 356 357 // We have a new symbol, select it. 358 if (cs.isPoisoned) { 359 import source.exception; 360 throw new CompileException(s.location, "Poisoned"); 361 } 362 363 cs.selected = ce.entry; 364 } 365 366 cs.set = newSet; 367 } 368 } 369 } 370 } 371 372 final: 373 374 /** 375 * A scope associate identifier with declarations. 376 */ 377 class NestedScope : Scope { 378 // TODO: Remove the module field, which can be access from fun. 379 Function fun; 380 381 mixin ScopeImpl!(ScopeType.Nested); 382 383 this(Function fun) { 384 this.fun = fun; 385 this.parentScope = fun; 386 } 387 388 this(NestedScope parentScope) { 389 this.fun = parentScope.fun; 390 this.parentScope = parentScope; 391 } 392 393 this(Scope s) { 394 if (auto n = cast(NestedScope) s) { 395 this(n); 396 } else if (auto f = cast(Function) s) { 397 this(f); 398 } else { 399 assert(0, "Parent scope must be a function or a nested scope"); 400 } 401 } 402 403 Module getModule() { 404 return fun.getModule(); 405 } 406 } 407 408 // XXX: Can't be private because mixin templates are a glorious hack. 409 // private: 410 class Poison : Symbol { 411 this(Location location, Name name) { 412 super(location, name); 413 } 414 } 415 416 struct ConditionalEntry { 417 Symbol entry; 418 ConditionalBranch[] cdBranches; 419 } 420 421 class ConditionalSet : Symbol { 422 ConditionalEntry[] set; 423 424 Symbol selected; 425 426 this(Location location, Name name, ConditionalEntry[] set) { 427 super(location, name); 428 this.set = set; 429 } 430 }