| /** |
| * Defines declarations of various attributes. |
| * |
| * The term 'attribute' refers to things that can apply to a larger scope than a single declaration. |
| * Among them are: |
| * - Alignment (`align(8)`) |
| * - User defined attributes (`@UDA`) |
| * - Function Attributes (`@safe`) |
| * - Storage classes (`static`, `__gshared`) |
| * - Mixin declarations (`mixin("int x;")`) |
| * - Conditional compilation (`static if`, `static foreach`) |
| * - Linkage (`extern(C)`) |
| * - Anonymous structs / unions |
| * - Protection (`private`, `public`) |
| * - Deprecated declarations (`@deprecated`) |
| * |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/attrib.d, _attrib.d) |
| * Documentation: https://dlang.org/phobos/dmd_attrib.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/attrib.d |
| */ |
| |
| module dmd.attrib; |
| |
| import dmd.aggregate; |
| import dmd.arraytypes; |
| import dmd.astenums; |
| import dmd.cond; |
| import dmd.declaration; |
| import dmd.dmodule; |
| import dmd.dscope; |
| import dmd.dsymbol; |
| import dmd.dsymbolsem : dsymbolSemantic; |
| import dmd.expression; |
| import dmd.expressionsem; |
| import dmd.func; |
| import dmd.globals; |
| import dmd.hdrgen : visibilityToBuffer; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.mtype; |
| import dmd.objc; // for objc.addSymbols |
| import dmd.common.outbuffer; |
| import dmd.root.array; // for each |
| import dmd.tokens; |
| import dmd.visitor; |
| |
| /*********************************************************** |
| * Abstract attribute applied to Dsymbol's used as a common |
| * ancestor for storage classes (StorageClassDeclaration), |
| * linkage (LinkageDeclaration) and others. |
| */ |
| extern (C++) abstract class AttribDeclaration : Dsymbol |
| { |
| Dsymbols* decl; /// Dsymbol's affected by this AttribDeclaration |
| |
| extern (D) this(Dsymbols* decl) |
| { |
| this.decl = decl; |
| } |
| |
| extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) |
| { |
| super(loc, ident); |
| this.decl = decl; |
| } |
| |
| Dsymbols* include(Scope* sc) |
| { |
| if (errors) |
| return null; |
| |
| return decl; |
| } |
| |
| /**************************************** |
| * Create a new scope if one or more given attributes |
| * are different from the sc's. |
| * If the returned scope != sc, the caller should pop |
| * the scope after it used. |
| */ |
| extern (D) static Scope* createNewScope(Scope* sc, StorageClass stc, LINK linkage, |
| CPPMANGLE cppmangle, Visibility visibility, int explicitVisibility, |
| AlignDeclaration aligndecl, PragmaDeclaration inlining) |
| { |
| Scope* sc2 = sc; |
| if (stc != sc.stc || |
| linkage != sc.linkage || |
| cppmangle != sc.cppmangle || |
| explicitVisibility != sc.explicitVisibility || |
| visibility != sc.visibility || |
| aligndecl !is sc.aligndecl || |
| inlining != sc.inlining) |
| { |
| // create new one for changes |
| sc2 = sc.copy(); |
| sc2.stc = stc; |
| sc2.linkage = linkage; |
| sc2.cppmangle = cppmangle; |
| sc2.visibility = visibility; |
| sc2.explicitVisibility = explicitVisibility; |
| sc2.aligndecl = aligndecl; |
| sc2.inlining = inlining; |
| } |
| return sc2; |
| } |
| |
| /**************************************** |
| * A hook point to supply scope for members. |
| * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. |
| */ |
| Scope* newScope(Scope* sc) |
| { |
| return sc; |
| } |
| |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| Dsymbols* d = include(sc); |
| if (d) |
| { |
| Scope* sc2 = newScope(sc); |
| d.foreachDsymbol( s => s.addMember(sc2, sds) ); |
| if (sc2 != sc) |
| sc2.pop(); |
| } |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| Dsymbols* d = include(sc); |
| //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d); |
| if (d) |
| { |
| Scope* sc2 = newScope(sc); |
| d.foreachDsymbol( s => s.setScope(sc2) ); |
| if (sc2 != sc) |
| sc2.pop(); |
| } |
| } |
| |
| override void importAll(Scope* sc) |
| { |
| Dsymbols* d = include(sc); |
| //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d); |
| if (d) |
| { |
| Scope* sc2 = newScope(sc); |
| d.foreachDsymbol( s => s.importAll(sc2) ); |
| if (sc2 != sc) |
| sc2.pop(); |
| } |
| } |
| |
| override void addComment(const(char)* comment) |
| { |
| //printf("AttribDeclaration::addComment %s\n", comment); |
| if (comment) |
| { |
| include(null).foreachDsymbol( s => s.addComment(comment) ); |
| } |
| } |
| |
| override const(char)* kind() const |
| { |
| return "attribute"; |
| } |
| |
| override bool oneMember(Dsymbol* ps, Identifier ident) |
| { |
| Dsymbols* d = include(null); |
| return Dsymbol.oneMembers(d, ps, ident); |
| } |
| |
| override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) |
| { |
| include(null).foreachDsymbol( s => s.setFieldOffset(ad, fieldState, isunion) ); |
| } |
| |
| override final bool hasPointers() |
| { |
| return include(null).foreachDsymbol( (s) { return s.hasPointers(); } ) != 0; |
| } |
| |
| override final bool hasStaticCtorOrDtor() |
| { |
| return include(null).foreachDsymbol( (s) { return s.hasStaticCtorOrDtor(); } ) != 0; |
| } |
| |
| override final void checkCtorConstInit() |
| { |
| include(null).foreachDsymbol( s => s.checkCtorConstInit() ); |
| } |
| |
| /**************************************** |
| */ |
| override final void addLocalClass(ClassDeclarations* aclasses) |
| { |
| include(null).foreachDsymbol( s => s.addLocalClass(aclasses) ); |
| } |
| |
| override final void addObjcSymbols(ClassDeclarations* classes, ClassDeclarations* categories) |
| { |
| objc.addSymbols(this, classes, categories); |
| } |
| |
| override final inout(AttribDeclaration) isAttribDeclaration() inout pure @safe |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Storage classes applied to Dsymbols, e.g. `const int i;` |
| * |
| * <stc> <decl...> |
| */ |
| extern (C++) class StorageClassDeclaration : AttribDeclaration |
| { |
| StorageClass stc; |
| |
| extern (D) this(StorageClass stc, Dsymbols* decl) |
| { |
| super(decl); |
| this.stc = stc; |
| } |
| |
| override StorageClassDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new StorageClassDeclaration(stc, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| StorageClass scstc = sc.stc; |
| /* These sets of storage classes are mutually exclusive, |
| * so choose the innermost or most recent one. |
| */ |
| if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest)) |
| scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.extern_ | STC.manifest); |
| if (stc & (STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared)) |
| scstc &= ~(STC.auto_ | STC.scope_ | STC.static_ | STC.manifest | STC.gshared); |
| if (stc & (STC.const_ | STC.immutable_ | STC.manifest)) |
| scstc &= ~(STC.const_ | STC.immutable_ | STC.manifest); |
| if (stc & (STC.gshared | STC.shared_)) |
| scstc &= ~(STC.gshared | STC.shared_); |
| if (stc & (STC.safe | STC.trusted | STC.system)) |
| scstc &= ~(STC.safe | STC.trusted | STC.system); |
| scstc |= stc; |
| //printf("scstc = x%llx\n", scstc); |
| return createNewScope(sc, scstc, sc.linkage, sc.cppmangle, |
| sc.visibility, sc.explicitVisibility, sc.aligndecl, sc.inlining); |
| } |
| |
| override final bool oneMember(Dsymbol* ps, Identifier ident) |
| { |
| bool t = Dsymbol.oneMembers(decl, ps, ident); |
| if (t && *ps) |
| { |
| /* This is to deal with the following case: |
| * struct Tick { |
| * template to(T) { const T to() { ... } } |
| * } |
| * For eponymous function templates, the 'const' needs to get attached to 'to' |
| * before the semantic analysis of 'to', so that template overloading based on the |
| * 'this' pointer can be successful. |
| */ |
| FuncDeclaration fd = (*ps).isFuncDeclaration(); |
| if (fd) |
| { |
| /* Use storage_class2 instead of storage_class otherwise when we do .di generation |
| * we'll wind up with 'const const' rather than 'const'. |
| */ |
| /* Don't think we need to worry about mutually exclusive storage classes here |
| */ |
| fd.storage_class2 |= stc; |
| } |
| } |
| return t; |
| } |
| |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| Dsymbols* d = include(sc); |
| if (d) |
| { |
| Scope* sc2 = newScope(sc); |
| |
| d.foreachDsymbol( (s) |
| { |
| //printf("\taddMember %s to %s\n", s.toChars(), sds.toChars()); |
| // STC.local needs to be attached before the member is added to the scope (because it influences the parent symbol) |
| if (auto decl = s.isDeclaration()) |
| { |
| decl.storage_class |= stc & STC.local; |
| if (auto sdecl = s.isStorageClassDeclaration()) // TODO: why is this not enough to deal with the nested case? |
| { |
| sdecl.stc |= stc & STC.local; |
| } |
| } |
| s.addMember(sc2, sds); |
| }); |
| |
| if (sc2 != sc) |
| sc2.pop(); |
| } |
| |
| } |
| |
| override inout(StorageClassDeclaration) isStorageClassDeclaration() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Deprecation with an additional message applied to Dsymbols, |
| * e.g. `deprecated("Superseeded by foo") int bar;`. |
| * (Note that `deprecated int bar;` is currently represented as a |
| * StorageClassDeclaration with STC.deprecated_) |
| * |
| * `deprecated(<msg>) <decl...>` |
| */ |
| extern (C++) final class DeprecatedDeclaration : StorageClassDeclaration |
| { |
| Expression msg; /// deprecation message |
| const(char)* msgstr; /// cached string representation of msg |
| |
| extern (D) this(Expression msg, Dsymbols* decl) |
| { |
| super(STC.deprecated_, decl); |
| this.msg = msg; |
| } |
| |
| override DeprecatedDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new DeprecatedDeclaration(msg.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| /** |
| * Provides a new scope with `STC.deprecated_` and `Scope.depdecl` set |
| * |
| * Calls `StorageClassDeclaration.newScope` (as it must be called or copied |
| * in any function overriding `newScope`), then set the `Scope`'s depdecl. |
| * |
| * Returns: |
| * Always a new scope, to use for this `DeprecatedDeclaration`'s members. |
| */ |
| override Scope* newScope(Scope* sc) |
| { |
| auto scx = super.newScope(sc); |
| // The enclosing scope is deprecated as well |
| if (scx == sc) |
| scx = sc.push(); |
| scx.depdecl = this; |
| return scx; |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| //printf("DeprecatedDeclaration::setScope() %p\n", this); |
| if (decl) |
| Dsymbol.setScope(sc); // for forward reference |
| return AttribDeclaration.setScope(sc); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Linkage attribute applied to Dsymbols, e.g. |
| * `extern(C) void foo()`. |
| * |
| * `extern(<linkage>) <decl...>` |
| */ |
| extern (C++) final class LinkDeclaration : AttribDeclaration |
| { |
| LINK linkage; /// either explicitly set or `default_` |
| |
| extern (D) this(const ref Loc loc, LINK linkage, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| //printf("LinkDeclaration(linkage = %d, decl = %p)\n", linkage, decl); |
| this.linkage = linkage; |
| } |
| |
| static LinkDeclaration create(const ref Loc loc, LINK p, Dsymbols* decl) |
| { |
| return new LinkDeclaration(loc, p, decl); |
| } |
| |
| override LinkDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new LinkDeclaration(loc, linkage, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| return createNewScope(sc, sc.stc, this.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, |
| sc.aligndecl, sc.inlining); |
| } |
| |
| override const(char)* toChars() const |
| { |
| return toString().ptr; |
| } |
| |
| extern(D) override const(char)[] toString() const |
| { |
| return "extern ()"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Attribute declaring whether an external aggregate should be mangled as |
| * a struct or class in C++, e.g. `extern(C++, struct) class C { ... }`. |
| * This is required for correct name mangling on MSVC targets, |
| * see cppmanglewin.d for details. |
| * |
| * `extern(C++, <cppmangle>) <decl...>` |
| */ |
| extern (C++) final class CPPMangleDeclaration : AttribDeclaration |
| { |
| CPPMANGLE cppmangle; |
| |
| extern (D) this(const ref Loc loc, CPPMANGLE cppmangle, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| //printf("CPPMangleDeclaration(cppmangle = %d, decl = %p)\n", cppmangle, decl); |
| this.cppmangle = cppmangle; |
| } |
| |
| override CPPMangleDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new CPPMangleDeclaration(loc, cppmangle, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| return createNewScope(sc, sc.stc, LINK.cpp, cppmangle, sc.visibility, sc.explicitVisibility, |
| sc.aligndecl, sc.inlining); |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| if (decl) |
| Dsymbol.setScope(sc); // for forward reference |
| return AttribDeclaration.setScope(sc); |
| } |
| |
| override const(char)* toChars() const |
| { |
| return toString().ptr; |
| } |
| |
| extern(D) override const(char)[] toString() const |
| { |
| return "extern ()"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /** |
| * A node to represent an `extern(C++)` namespace attribute |
| * |
| * There are two ways to declarate a symbol as member of a namespace: |
| * `Nspace` and `CPPNamespaceDeclaration`. |
| * The former creates a scope for the symbol, and inject them in the |
| * parent scope at the same time. |
| * The later, this class, has no semantic implications and is only |
| * used for mangling. |
| * Additionally, this class allows one to use reserved identifiers |
| * (D keywords) in the namespace. |
| * |
| * A `CPPNamespaceDeclaration` can be created from an `Identifier` |
| * (already resolved) or from an `Expression`, which is CTFE-ed |
| * and can be either a `TupleExp`, in which can additional |
| * `CPPNamespaceDeclaration` nodes are created, or a `StringExp`. |
| * |
| * Note that this class, like `Nspace`, matches only one identifier |
| * part of a namespace. For the namespace `"foo::bar"`, |
| * the will be a `CPPNamespaceDeclaration` with its `ident` |
| * set to `"bar"`, and its `namespace` field pointing to another |
| * `CPPNamespaceDeclaration` with its `ident` set to `"foo"`. |
| */ |
| extern (C++) final class CPPNamespaceDeclaration : AttribDeclaration |
| { |
| /// CTFE-able expression, resolving to `TupleExp` or `StringExp` |
| Expression exp; |
| |
| extern (D) this(const ref Loc loc, Identifier ident, Dsymbols* decl) |
| { |
| super(loc, ident, decl); |
| } |
| |
| extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.exp = exp; |
| } |
| |
| extern (D) this(const ref Loc loc, Identifier ident, Expression exp, Dsymbols* decl, |
| CPPNamespaceDeclaration parent) |
| { |
| super(loc, ident, decl); |
| this.exp = exp; |
| this.cppnamespace = parent; |
| } |
| |
| override CPPNamespaceDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new CPPNamespaceDeclaration( |
| this.loc, this.ident, this.exp, Dsymbol.arraySyntaxCopy(this.decl), this.cppnamespace); |
| } |
| |
| /** |
| * Returns: |
| * A copy of the parent scope, with `this` as `namespace` and C++ linkage |
| */ |
| override Scope* newScope(Scope* sc) |
| { |
| auto scx = sc.copy(); |
| scx.linkage = LINK.cpp; |
| scx.namespace = this; |
| return scx; |
| } |
| |
| override const(char)* toChars() const |
| { |
| return toString().ptr; |
| } |
| |
| extern(D) override const(char)[] toString() const |
| { |
| return "extern (C++, `namespace`)"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| |
| override inout(CPPNamespaceDeclaration) isCPPNamespaceDeclaration() inout { return this; } |
| } |
| |
| /*********************************************************** |
| * Visibility declaration for Dsymbols, e.g. `public int i;` |
| * |
| * `<visibility> <decl...>` or |
| * `package(<pkg_identifiers>) <decl...>` if `pkg_identifiers !is null` |
| */ |
| extern (C++) final class VisibilityDeclaration : AttribDeclaration |
| { |
| Visibility visibility; /// the visibility |
| Identifier[] pkg_identifiers; /// identifiers for `package(foo.bar)` or null |
| |
| /** |
| * Params: |
| * loc = source location of attribute token |
| * visibility = visibility attribute data |
| * decl = declarations which are affected by this visibility attribute |
| */ |
| extern (D) this(const ref Loc loc, Visibility visibility, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.visibility = visibility; |
| //printf("decl = %p\n", decl); |
| } |
| |
| /** |
| * Params: |
| * loc = source location of attribute token |
| * pkg_identifiers = list of identifiers for a qualified package name |
| * decl = declarations which are affected by this visibility attribute |
| */ |
| extern (D) this(const ref Loc loc, Identifier[] pkg_identifiers, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.visibility.kind = Visibility.Kind.package_; |
| this.pkg_identifiers = pkg_identifiers; |
| if (pkg_identifiers.length > 0) |
| { |
| Dsymbol tmp; |
| Package.resolve(pkg_identifiers, &tmp, null); |
| visibility.pkg = tmp ? tmp.isPackage() : null; |
| } |
| } |
| |
| override VisibilityDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| |
| if (visibility.kind == Visibility.Kind.package_) |
| return new VisibilityDeclaration(this.loc, pkg_identifiers, Dsymbol.arraySyntaxCopy(decl)); |
| else |
| return new VisibilityDeclaration(this.loc, visibility, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| if (pkg_identifiers) |
| dsymbolSemantic(this, sc); |
| return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, this.visibility, 1, sc.aligndecl, sc.inlining); |
| } |
| |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| if (pkg_identifiers) |
| { |
| Dsymbol tmp; |
| Package.resolve(pkg_identifiers, &tmp, null); |
| visibility.pkg = tmp ? tmp.isPackage() : null; |
| pkg_identifiers = null; |
| } |
| if (visibility.kind == Visibility.Kind.package_ && visibility.pkg && sc._module) |
| { |
| Module m = sc._module; |
| |
| // While isAncestorPackageOf does an equality check, the fix for issue 17441 adds a check to see if |
| // each package's .isModule() properites are equal. |
| // |
| // Properties generated from `package(foo)` i.e. visibility.pkg have .isModule() == null. |
| // This breaks package declarations of the package in question if they are declared in |
| // the same package.d file, which _do_ have a module associated with them, and hence a non-null |
| // isModule() |
| if (!m.isPackage() || !visibility.pkg.ident.equals(m.isPackage().ident)) |
| { |
| Package pkg = m.parent ? m.parent.isPackage() : null; |
| if (!pkg || !visibility.pkg.isAncestorPackageOf(pkg)) |
| error("does not bind to one of ancestor packages of module `%s`", m.toPrettyChars(true)); |
| } |
| } |
| return AttribDeclaration.addMember(sc, sds); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "visibility attribute"; |
| } |
| |
| override const(char)* toPrettyChars(bool) |
| { |
| assert(visibility.kind > Visibility.Kind.undefined); |
| OutBuffer buf; |
| visibilityToBuffer(&buf, visibility); |
| return buf.extractChars(); |
| } |
| |
| override inout(VisibilityDeclaration) isVisibilityDeclaration() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Alignment attribute for aggregates, members and variables. |
| * |
| * `align(<ealign>) <decl...>` or |
| * `align <decl...>` if `ealign` is null |
| */ |
| extern (C++) final class AlignDeclaration : AttribDeclaration |
| { |
| Expressions* exps; /// Expression(s) yielding the desired alignment, |
| /// the largest value wins |
| /// the actual alignment is Unknown until it's either set to the value of `ealign` |
| /// or the default if `ealign` is null ( / an error ocurred) |
| structalign_t salign; |
| |
| |
| extern (D) this(const ref Loc loc, Expression exp, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| if (exp) |
| { |
| exps = new Expressions(); |
| exps.push(exp); |
| } |
| } |
| |
| extern (D) this(const ref Loc loc, Expressions* exps, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.exps = exps; |
| } |
| |
| extern (D) this(const ref Loc loc, structalign_t salign, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.salign = salign; |
| } |
| |
| override AlignDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new AlignDeclaration(loc, |
| Expression.arraySyntaxCopy(exps), |
| Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, this, sc.inlining); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * An anonymous struct/union (defined by `isunion`). |
| */ |
| extern (C++) final class AnonDeclaration : AttribDeclaration |
| { |
| bool isunion; /// whether it's a union |
| int sem; /// 1 if successful semantic() |
| uint anonoffset; /// offset of anonymous struct |
| uint anonstructsize; /// size of anonymous struct |
| uint anonalignsize; /// size of anonymous struct for alignment purposes |
| |
| extern (D) this(const ref Loc loc, bool isunion, Dsymbols* decl) |
| { |
| super(loc, null, decl); |
| this.isunion = isunion; |
| } |
| |
| override AnonDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new AnonDeclaration(loc, isunion, Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| if (decl) |
| Dsymbol.setScope(sc); |
| return AttribDeclaration.setScope(sc); |
| } |
| |
| override void setFieldOffset(AggregateDeclaration ad, ref FieldState fieldState, bool isunion) |
| { |
| //printf("\tAnonDeclaration::setFieldOffset %s %p\n", isunion ? "union" : "struct", this); |
| if (decl) |
| { |
| /* This works by treating an AnonDeclaration as an aggregate 'member', |
| * so in order to place that member we need to compute the member's |
| * size and alignment. |
| */ |
| size_t fieldstart = ad.fields.dim; |
| |
| /* Hackishly hijack ad's structsize and alignsize fields |
| * for use in our fake anon aggregate member. |
| */ |
| uint savestructsize = ad.structsize; |
| uint savealignsize = ad.alignsize; |
| ad.structsize = 0; |
| ad.alignsize = 0; |
| |
| FieldState fs; |
| decl.foreachDsymbol( (s) |
| { |
| s.setFieldOffset(ad, fs, this.isunion); |
| if (this.isunion) |
| fs.offset = 0; |
| }); |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=13613 |
| * If the fields in this.members had been already |
| * added in ad.fields, just update *poffset for the subsequent |
| * field offset calculation. |
| */ |
| if (fieldstart == ad.fields.dim) |
| { |
| ad.structsize = savestructsize; |
| ad.alignsize = savealignsize; |
| fieldState.offset = ad.structsize; |
| return; |
| } |
| |
| anonstructsize = ad.structsize; |
| anonalignsize = ad.alignsize; |
| ad.structsize = savestructsize; |
| ad.alignsize = savealignsize; |
| |
| // 0 sized structs are set to 1 byte |
| if (anonstructsize == 0) |
| { |
| anonstructsize = 1; |
| anonalignsize = 1; |
| } |
| |
| assert(_scope); |
| auto alignment = _scope.alignment(); |
| |
| /* Given the anon 'member's size and alignment, |
| * go ahead and place it. |
| */ |
| anonoffset = AggregateDeclaration.placeField( |
| &fieldState.offset, |
| anonstructsize, anonalignsize, alignment, |
| &ad.structsize, &ad.alignsize, |
| isunion); |
| |
| // Add to the anon fields the base offset of this anonymous aggregate |
| //printf("anon fields, anonoffset = %d\n", anonoffset); |
| foreach (const i; fieldstart .. ad.fields.dim) |
| { |
| VarDeclaration v = ad.fields[i]; |
| //printf("\t[%d] %s %d\n", i, v.toChars(), v.offset); |
| v.offset += anonoffset; |
| } |
| } |
| } |
| |
| override const(char)* kind() const |
| { |
| return (isunion ? "anonymous union" : "anonymous struct"); |
| } |
| |
| override inout(AnonDeclaration) isAnonDeclaration() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Pragma applied to Dsymbols, e.g. `pragma(inline, true) void foo`, |
| * but not PragmaStatement's like `pragma(msg, "hello");`. |
| * |
| * pragma(<ident>, <args>) |
| */ |
| extern (C++) final class PragmaDeclaration : AttribDeclaration |
| { |
| Expressions* args; /// parameters of this pragma |
| |
| extern (D) this(const ref Loc loc, Identifier ident, Expressions* args, Dsymbols* decl) |
| { |
| super(loc, ident, decl); |
| this.args = args; |
| } |
| |
| override PragmaDeclaration syntaxCopy(Dsymbol s) |
| { |
| //printf("PragmaDeclaration::syntaxCopy(%s)\n", toChars()); |
| assert(!s); |
| return new PragmaDeclaration(loc, ident, Expression.arraySyntaxCopy(args), Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| if (ident == Id.Pinline) |
| { |
| // We keep track of this pragma inside scopes, |
| // then it's evaluated on demand in function semantic |
| return createNewScope(sc, sc.stc, sc.linkage, sc.cppmangle, sc.visibility, sc.explicitVisibility, sc.aligndecl, this); |
| } |
| return sc; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "pragma"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * A conditional compilation declaration, used for `version` |
| * / `debug` and specialized for `static if`. |
| * |
| * <condition> { <decl...> } else { <elsedecl> } |
| */ |
| extern (C++) class ConditionalDeclaration : AttribDeclaration |
| { |
| Condition condition; /// condition deciding whether decl or elsedecl applies |
| Dsymbols* elsedecl; /// array of Dsymbol's for else block |
| |
| extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) |
| { |
| super(loc, null, decl); |
| //printf("ConditionalDeclaration::ConditionalDeclaration()\n"); |
| this.condition = condition; |
| this.elsedecl = elsedecl; |
| } |
| |
| override ConditionalDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new ConditionalDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); |
| } |
| |
| override final bool oneMember(Dsymbol* ps, Identifier ident) |
| { |
| //printf("ConditionalDeclaration::oneMember(), inc = %d\n", condition.inc); |
| if (condition.inc != Include.notComputed) |
| { |
| Dsymbols* d = condition.include(null) ? decl : elsedecl; |
| return Dsymbol.oneMembers(d, ps, ident); |
| } |
| else |
| { |
| bool res = (Dsymbol.oneMembers(decl, ps, ident) && *ps is null && Dsymbol.oneMembers(elsedecl, ps, ident) && *ps is null); |
| *ps = null; |
| return res; |
| } |
| } |
| |
| // Decide if 'then' or 'else' code should be included |
| override Dsymbols* include(Scope* sc) |
| { |
| //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, _scope); |
| |
| if (errors) |
| return null; |
| |
| assert(condition); |
| return condition.include(_scope ? _scope : sc) ? decl : elsedecl; |
| } |
| |
| override final void addComment(const(char)* comment) |
| { |
| /* Because addComment is called by the parser, if we called |
| * include() it would define a version before it was used. |
| * But it's no problem to drill down to both decl and elsedecl, |
| * so that's the workaround. |
| */ |
| if (comment) |
| { |
| decl .foreachDsymbol( s => s.addComment(comment) ); |
| elsedecl.foreachDsymbol( s => s.addComment(comment) ); |
| } |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| include(sc).foreachDsymbol( s => s.setScope(sc) ); |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * `<scopesym> { |
| * static if (<condition>) { <decl> } else { <elsedecl> } |
| * }` |
| */ |
| extern (C++) final class StaticIfDeclaration : ConditionalDeclaration |
| { |
| ScopeDsymbol scopesym; /// enclosing symbol (e.g. module) where symbols will be inserted |
| private bool addisdone = false; /// true if members have been added to scope |
| private bool onStack = false; /// true if a call to `include` is currently active |
| |
| extern (D) this(const ref Loc loc, Condition condition, Dsymbols* decl, Dsymbols* elsedecl) |
| { |
| super(loc, condition, decl, elsedecl); |
| //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); |
| } |
| |
| override StaticIfDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new StaticIfDeclaration(loc, condition.syntaxCopy(), Dsymbol.arraySyntaxCopy(decl), Dsymbol.arraySyntaxCopy(elsedecl)); |
| } |
| |
| /**************************************** |
| * Different from other AttribDeclaration subclasses, include() call requires |
| * the completion of addMember and setScope phases. |
| */ |
| override Dsymbols* include(Scope* sc) |
| { |
| //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, _scope); |
| |
| if (errors || onStack) |
| return null; |
| onStack = true; |
| scope(exit) onStack = false; |
| |
| if (sc && condition.inc == Include.notComputed) |
| { |
| assert(scopesym); // addMember is already done |
| assert(_scope); // setScope is already done |
| Dsymbols* d = ConditionalDeclaration.include(_scope); |
| if (d && !addisdone) |
| { |
| // Add members lazily. |
| d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); |
| |
| // Set the member scopes lazily. |
| d.foreachDsymbol( s => s.setScope(_scope) ); |
| |
| addisdone = true; |
| } |
| return d; |
| } |
| else |
| { |
| return ConditionalDeclaration.include(sc); |
| } |
| } |
| |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| //printf("StaticIfDeclaration::addMember() '%s'\n", toChars()); |
| /* This is deferred until the condition evaluated later (by the include() call), |
| * so that expressions in the condition can refer to declarations |
| * in the same scope, such as: |
| * |
| * template Foo(int i) |
| * { |
| * const int j = i + 1; |
| * static if (j == 3) |
| * const int k; |
| * } |
| */ |
| this.scopesym = sds; |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| // do not evaluate condition before semantic pass |
| // But do set the scope, in case we need it for forward referencing |
| Dsymbol.setScope(sc); |
| } |
| |
| override void importAll(Scope* sc) |
| { |
| // do not evaluate condition before semantic pass |
| } |
| |
| override const(char)* kind() const |
| { |
| return "static if"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Static foreach at declaration scope, like: |
| * static foreach (i; [0, 1, 2]){ } |
| */ |
| |
| extern (C++) final class StaticForeachDeclaration : AttribDeclaration |
| { |
| StaticForeach sfe; /// contains `static foreach` expansion logic |
| |
| ScopeDsymbol scopesym; /// cached enclosing scope (mimics `static if` declaration) |
| |
| /++ |
| `include` can be called multiple times, but a `static foreach` |
| should be expanded at most once. Achieved by caching the result |
| of the first call. We need both `cached` and `cache`, because |
| `null` is a valid value for `cache`. |
| +/ |
| bool onStack = false; |
| bool cached = false; |
| Dsymbols* cache = null; |
| |
| extern (D) this(StaticForeach sfe, Dsymbols* decl) |
| { |
| super(sfe.loc, null, decl); |
| this.sfe = sfe; |
| } |
| |
| override StaticForeachDeclaration syntaxCopy(Dsymbol s) |
| { |
| assert(!s); |
| return new StaticForeachDeclaration( |
| sfe.syntaxCopy(), |
| Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override bool oneMember(Dsymbol* ps, Identifier ident) |
| { |
| // Required to support IFTI on a template that contains a |
| // `static foreach` declaration. `super.oneMember` calls |
| // include with a `null` scope. As `static foreach` requires |
| // the scope for expansion, `oneMember` can only return a |
| // precise result once `static foreach` has been expanded. |
| if (cached) |
| { |
| return super.oneMember(ps, ident); |
| } |
| *ps = null; // a `static foreach` declaration may in general expand to multiple symbols |
| return false; |
| } |
| |
| override Dsymbols* include(Scope* sc) |
| { |
| if (errors || onStack) |
| return null; |
| if (cached) |
| { |
| assert(!onStack); |
| return cache; |
| } |
| onStack = true; |
| scope(exit) onStack = false; |
| |
| if (_scope) |
| { |
| sfe.prepare(_scope); // lower static foreach aggregate |
| } |
| if (!sfe.ready()) |
| { |
| return null; // TODO: ok? |
| } |
| |
| // expand static foreach |
| import dmd.statementsem: makeTupleForeach; |
| Dsymbols* d = makeTupleForeach(_scope, true, true, sfe.aggrfe, decl, sfe.needExpansion).decl; |
| if (d) // process generated declarations |
| { |
| // Add members lazily. |
| d.foreachDsymbol( s => s.addMember(_scope, scopesym) ); |
| |
| // Set the member scopes lazily. |
| d.foreachDsymbol( s => s.setScope(_scope) ); |
| } |
| cached = true; |
| cache = d; |
| return d; |
| } |
| |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| // used only for caching the enclosing symbol |
| this.scopesym = sds; |
| } |
| |
| override void addComment(const(char)* comment) |
| { |
| // do nothing |
| // change this to give semantics to documentation comments on static foreach declarations |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| // do not evaluate condition before semantic pass |
| // But do set the scope, in case we need it for forward referencing |
| Dsymbol.setScope(sc); |
| } |
| |
| override void importAll(Scope* sc) |
| { |
| // do not evaluate aggregate before semantic pass |
| } |
| |
| override const(char)* kind() const |
| { |
| return "static foreach"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * Collection of declarations that stores foreach index variables in a |
| * local symbol table. Other symbols declared within are forwarded to |
| * another scope, like: |
| * |
| * static foreach (i; 0 .. 10) // loop variables for different indices do not conflict. |
| * { // this body is expanded into 10 ForwardingAttribDeclarations, where `i` has storage class STC.local |
| * mixin("enum x" ~ to!string(i) ~ " = i"); // ok, can access current loop variable |
| * } |
| * |
| * static foreach (i; 0.. 10) |
| * { |
| * pragma(msg, mixin("x" ~ to!string(i))); // ok, all 10 symbols are visible as they were forwarded to the global scope |
| * } |
| * |
| * static assert (!is(typeof(i))); // loop index variable is not visible outside of the static foreach loop |
| * |
| * A StaticForeachDeclaration generates one |
| * ForwardingAttribDeclaration for each expansion of its body. The |
| * AST of the ForwardingAttribDeclaration contains both the `static |
| * foreach` variables and the respective copy of the `static foreach` |
| * body. The functionality is achieved by using a |
| * ForwardingScopeDsymbol as the parent symbol for the generated |
| * declarations. |
| */ |
| |
| extern(C++) final class ForwardingAttribDeclaration : AttribDeclaration |
| { |
| ForwardingScopeDsymbol sym = null; |
| |
| this(Dsymbols* decl) |
| { |
| super(decl); |
| sym = new ForwardingScopeDsymbol(); |
| sym.symtab = new DsymbolTable(); |
| } |
| |
| /************************************** |
| * Use the ForwardingScopeDsymbol as the parent symbol for members. |
| */ |
| override Scope* newScope(Scope* sc) |
| { |
| return sc.push(sym); |
| } |
| |
| /*************************************** |
| * Lazily initializes the scope to forward to. |
| */ |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| sym.parent = sds; |
| return super.addMember(sc, sym); |
| } |
| |
| override inout(ForwardingAttribDeclaration) isForwardingAttribDeclaration() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| |
| /*********************************************************** |
| * Mixin declarations, like: |
| * mixin("int x"); |
| * https://dlang.org/spec/module.html#mixin-declaration |
| */ |
| extern (C++) final class CompileDeclaration : AttribDeclaration |
| { |
| Expressions* exps; |
| ScopeDsymbol scopesym; |
| bool compiled; |
| |
| extern (D) this(const ref Loc loc, Expressions* exps) |
| { |
| super(loc, null, null); |
| //printf("CompileDeclaration(loc = %d)\n", loc.linnum); |
| this.exps = exps; |
| } |
| |
| override CompileDeclaration syntaxCopy(Dsymbol s) |
| { |
| //printf("CompileDeclaration::syntaxCopy('%s')\n", toChars()); |
| return new CompileDeclaration(loc, Expression.arraySyntaxCopy(exps)); |
| } |
| |
| override void addMember(Scope* sc, ScopeDsymbol sds) |
| { |
| //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); |
| this.scopesym = sds; |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| Dsymbol.setScope(sc); |
| } |
| |
| override const(char)* kind() const |
| { |
| return "mixin"; |
| } |
| |
| override inout(CompileDeclaration) isCompileDeclaration() inout |
| { |
| return this; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| } |
| |
| /*********************************************************** |
| * User defined attributes look like: |
| * @foo(args, ...) |
| * @(args, ...) |
| */ |
| extern (C++) final class UserAttributeDeclaration : AttribDeclaration |
| { |
| Expressions* atts; |
| |
| extern (D) this(Expressions* atts, Dsymbols* decl) |
| { |
| super(decl); |
| this.atts = atts; |
| } |
| |
| override UserAttributeDeclaration syntaxCopy(Dsymbol s) |
| { |
| //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars()); |
| assert(!s); |
| return new UserAttributeDeclaration(Expression.arraySyntaxCopy(this.atts), Dsymbol.arraySyntaxCopy(decl)); |
| } |
| |
| override Scope* newScope(Scope* sc) |
| { |
| Scope* sc2 = sc; |
| if (atts && atts.dim) |
| { |
| // create new one for changes |
| sc2 = sc.copy(); |
| sc2.userAttribDecl = this; |
| } |
| return sc2; |
| } |
| |
| override void setScope(Scope* sc) |
| { |
| //printf("UserAttributeDeclaration::setScope() %p\n", this); |
| if (decl) |
| Dsymbol.setScope(sc); // for forward reference of UDAs |
| return AttribDeclaration.setScope(sc); |
| } |
| |
| extern (D) static Expressions* concat(Expressions* udas1, Expressions* udas2) |
| { |
| Expressions* udas; |
| if (!udas1 || udas1.dim == 0) |
| udas = udas2; |
| else if (!udas2 || udas2.dim == 0) |
| udas = udas1; |
| else |
| { |
| /* Create a new tuple that combines them |
| * (do not append to left operand, as this is a copy-on-write operation) |
| */ |
| udas = new Expressions(2); |
| (*udas)[0] = new TupleExp(Loc.initial, udas1); |
| (*udas)[1] = new TupleExp(Loc.initial, udas2); |
| } |
| return udas; |
| } |
| |
| Expressions* getAttributes() |
| { |
| if (auto sc = _scope) |
| { |
| _scope = null; |
| arrayExpressionSemantic(atts.peekSlice(), sc); |
| } |
| auto exps = new Expressions(); |
| if (userAttribDecl && userAttribDecl !is this) |
| exps.push(new TupleExp(Loc.initial, userAttribDecl.getAttributes())); |
| if (atts && atts.dim) |
| exps.push(new TupleExp(Loc.initial, atts)); |
| return exps; |
| } |
| |
| override const(char)* kind() const |
| { |
| return "UserAttribute"; |
| } |
| |
| override void accept(Visitor v) |
| { |
| v.visit(this); |
| } |
| |
| /** |
| * Check if the provided expression references `core.attribute.gnuAbiTag` |
| * |
| * This should be called after semantic has been run on the expression. |
| * Semantic on UDA happens in semantic2 (see `dmd.semantic2`). |
| * |
| * Params: |
| * e = Expression to check (usually from `UserAttributeDeclaration.atts`) |
| * |
| * Returns: |
| * `true` if the expression references the compiler-recognized `gnuAbiTag` |
| */ |
| static bool isGNUABITag(Expression e) |
| { |
| if (global.params.cplusplus < CppStdRevision.cpp11) |
| return false; |
| |
| auto ts = e.type ? e.type.isTypeStruct() : null; |
| if (!ts) |
| return false; |
| if (ts.sym.ident != Id.udaGNUAbiTag || !ts.sym.parent) |
| return false; |
| // Can only be defined in druntime |
| Module m = ts.sym.parent.isModule(); |
| if (!m || !m.isCoreModule(Id.attribute)) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Called from a symbol's semantic to check if `gnuAbiTag` UDA |
| * can be applied to them |
| * |
| * Directly emits an error if the UDA doesn't work with this symbol |
| * |
| * Params: |
| * sym = symbol to check for `gnuAbiTag` |
| * linkage = Linkage of the symbol (Declaration.link or sc.link) |
| */ |
| static void checkGNUABITag(Dsymbol sym, LINK linkage) |
| { |
| if (global.params.cplusplus < CppStdRevision.cpp11) |
| return; |
| |
| foreachUdaNoSemantic(sym, (exp) { |
| if (isGNUABITag(exp)) |
| { |
| if (sym.isCPPNamespaceDeclaration() || sym.isNspace()) |
| { |
| exp.error("`@%s` cannot be applied to namespaces", Id.udaGNUAbiTag.toChars()); |
| sym.errors = true; |
| } |
| else if (linkage != LINK.cpp) |
| { |
| exp.error("`@%s` can only apply to C++ symbols", Id.udaGNUAbiTag.toChars()); |
| sym.errors = true; |
| } |
| // Only one `@gnuAbiTag` is allowed by semantic2 |
| return 1; // break |
| } |
| return 0; // continue |
| }); |
| } |
| } |
| |
| /** |
| * Returns `true` if the given symbol is a symbol declared in |
| * `core.attribute` and has the given identifier. |
| * |
| * This is used to determine if a symbol is a UDA declared in |
| * `core.attribute`. |
| * |
| * Params: |
| * sym = the symbol to check |
| * ident = the name of the expected UDA |
| */ |
| bool isCoreUda(Dsymbol sym, Identifier ident) |
| { |
| if (sym.ident != ident || !sym.parent) |
| return false; |
| |
| auto _module = sym.parent.isModule(); |
| return _module && _module.isCoreModule(Id.attribute); |
| } |
| |
| /** |
| * Iterates the UDAs attached to the given symbol. |
| * |
| * Params: |
| * sym = the symbol to get the UDAs from |
| * sc = scope to use for semantic analysis of UDAs |
| * dg = called once for each UDA |
| * |
| * Returns: |
| * If `dg` returns `!= 0`, stops the iteration and returns that value. |
| * Otherwise, returns 0. |
| */ |
| int foreachUda(Dsymbol sym, Scope* sc, int delegate(Expression) dg) |
| { |
| if (!sym.userAttribDecl) |
| return 0; |
| |
| auto udas = sym.userAttribDecl.getAttributes(); |
| arrayExpressionSemantic(udas.peekSlice(), sc, true); |
| |
| return udas.each!((uda) { |
| if (!uda.isTupleExp()) |
| return 0; |
| |
| auto exps = uda.isTupleExp().exps; |
| |
| return exps.each!((e) { |
| assert(e); |
| |
| if (auto result = dg(e)) |
| return result; |
| |
| return 0; |
| }); |
| }); |
| } |
| |
| /** |
| * Iterates the UDAs attached to the given symbol, without performing semantic |
| * analysis. |
| * |
| * Use this function instead of `foreachUda` if semantic analysis of `sym` is |
| * still in progress. |
| * |
| * Params: |
| * sym = the symbol to get the UDAs from |
| * dg = called once for each UDA |
| * |
| * Returns: |
| * If `dg` returns `!= 0`, stops the iteration and returns that value. |
| * Otherwise, returns 0. |
| */ |
| int foreachUdaNoSemantic(Dsymbol sym, int delegate(Expression) dg) |
| { |
| if (sym.userAttribDecl is null || sym.userAttribDecl.atts is null) |
| return 0; |
| |
| foreach (exp; *sym.userAttribDecl.atts) |
| { |
| if (auto result = dg(exp)) |
| return result; |
| } |
| |
| return 0; |
| } |