| /** |
| * Performs the semantic2 stage, which deals with initializer expressions. |
| * |
| * 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/semantic2.d, _semantic2.d) |
| * Documentation: https://dlang.org/phobos/dmd_semantic2.html |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/semantic2.d |
| */ |
| |
| module dmd.semantic2; |
| |
| import core.stdc.stdio; |
| import core.stdc.string; |
| |
| import dmd.aggregate; |
| import dmd.aliasthis; |
| import dmd.arraytypes; |
| import dmd.astcodegen; |
| import dmd.astenums; |
| import dmd.attrib; |
| import dmd.blockexit; |
| import dmd.clone; |
| import dmd.dcast; |
| import dmd.dclass; |
| import dmd.declaration; |
| import dmd.denum; |
| import dmd.dimport; |
| import dmd.dinterpret; |
| import dmd.dmodule; |
| import dmd.dscope; |
| import dmd.dstruct; |
| import dmd.dsymbol; |
| import dmd.dsymbolsem; |
| import dmd.dtemplate; |
| import dmd.dversion; |
| import dmd.errors; |
| import dmd.escape; |
| import dmd.expression; |
| import dmd.expressionsem; |
| import dmd.func; |
| import dmd.globals; |
| import dmd.id; |
| import dmd.identifier; |
| import dmd.init; |
| import dmd.initsem; |
| import dmd.hdrgen; |
| import dmd.mtype; |
| import dmd.nogc; |
| import dmd.nspace; |
| import dmd.objc; |
| import dmd.opover; |
| import dmd.parse; |
| import dmd.root.filename; |
| import dmd.common.outbuffer; |
| import dmd.root.rmem; |
| import dmd.root.rootobject; |
| import dmd.root.utf; |
| import dmd.sideeffect; |
| import dmd.statementsem; |
| import dmd.staticassert; |
| import dmd.tokens; |
| import dmd.statement; |
| import dmd.target; |
| import dmd.templateparamsem; |
| import dmd.typesem; |
| import dmd.visitor; |
| |
| enum LOG = false; |
| |
| |
| /************************************* |
| * Does semantic analysis on initializers and members of aggregates. |
| */ |
| extern(C++) void semantic2(Dsymbol dsym, Scope* sc) |
| { |
| scope v = new Semantic2Visitor(sc); |
| dsym.accept(v); |
| } |
| |
| private extern(C++) final class Semantic2Visitor : Visitor |
| { |
| alias visit = Visitor.visit; |
| Scope* sc; |
| this(Scope* sc) |
| { |
| this.sc = sc; |
| } |
| |
| override void visit(Dsymbol) {} |
| |
| override void visit(StaticAssert sa) |
| { |
| //printf("StaticAssert::semantic2() %s\n", sa.toChars()); |
| auto sds = new ScopeDsymbol(); |
| sc = sc.push(sds); |
| sc.tinst = null; |
| sc.minst = null; |
| |
| import dmd.staticcond; |
| bool errors; |
| bool result = evalStaticCondition(sc, sa.exp, sa.exp, errors); |
| sc = sc.pop(); |
| if (errors) |
| { |
| errorSupplemental(sa.loc, "while evaluating: `static assert(%s)`", sa.exp.toChars()); |
| return; |
| } |
| else if (result) |
| return; |
| |
| if (sa.msg) |
| { |
| sc = sc.startCTFE(); |
| sa.msg = sa.msg.expressionSemantic(sc); |
| sa.msg = resolveProperties(sc, sa.msg); |
| sc = sc.endCTFE(); |
| sa.msg = sa.msg.ctfeInterpret(); |
| if (StringExp se = sa.msg.toStringExp()) |
| { |
| // same with pragma(msg) |
| const slice = se.toUTF8(sc).peekString(); |
| error(sa.loc, "static assert: \"%.*s\"", cast(int)slice.length, slice.ptr); |
| } |
| else |
| error(sa.loc, "static assert: %s", sa.msg.toChars()); |
| } |
| else |
| error(sa.loc, "static assert: `%s` is false", sa.exp.toChars()); |
| if (sc.tinst) |
| sc.tinst.printInstantiationTrace(); |
| if (!global.gag) |
| fatal(); |
| } |
| |
| override void visit(TemplateInstance tempinst) |
| { |
| if (tempinst.semanticRun >= PASS.semantic2) |
| return; |
| tempinst.semanticRun = PASS.semantic2; |
| static if (LOG) |
| { |
| printf("+TemplateInstance.semantic2('%s')\n", tempinst.toChars()); |
| scope(exit) printf("-TemplateInstance.semantic2('%s')\n", tempinst.toChars()); |
| } |
| if (tempinst.errors || !tempinst.members) |
| return; |
| |
| TemplateDeclaration tempdecl = tempinst.tempdecl.isTemplateDeclaration(); |
| assert(tempdecl); |
| |
| sc = tempdecl._scope; |
| assert(sc); |
| sc = sc.push(tempinst.argsym); |
| sc = sc.push(tempinst); |
| sc.tinst = tempinst; |
| sc.minst = tempinst.minst; |
| |
| int needGagging = (tempinst.gagged && !global.gag); |
| uint olderrors = global.errors; |
| int oldGaggedErrors = -1; // dead-store to prevent spurious warning |
| if (needGagging) |
| oldGaggedErrors = global.startGagging(); |
| |
| for (size_t i = 0; i < tempinst.members.dim; i++) |
| { |
| Dsymbol s = (*tempinst.members)[i]; |
| static if (LOG) |
| { |
| printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind()); |
| } |
| s.semantic2(sc); |
| if (tempinst.gagged && global.errors != olderrors) |
| break; |
| } |
| |
| if (global.errors != olderrors) |
| { |
| if (!tempinst.errors) |
| { |
| if (!tempdecl.literal) |
| tempinst.error(tempinst.loc, "error instantiating"); |
| if (tempinst.tinst) |
| tempinst.tinst.printInstantiationTrace(); |
| } |
| tempinst.errors = true; |
| } |
| if (needGagging) |
| global.endGagging(oldGaggedErrors); |
| |
| sc = sc.pop(); |
| sc.pop(); |
| } |
| |
| override void visit(TemplateMixin tmix) |
| { |
| if (tmix.semanticRun >= PASS.semantic2) |
| return; |
| tmix.semanticRun = PASS.semantic2; |
| static if (LOG) |
| { |
| printf("+TemplateMixin.semantic2('%s')\n", tmix.toChars()); |
| scope(exit) printf("-TemplateMixin.semantic2('%s')\n", tmix.toChars()); |
| } |
| if (!tmix.members) |
| return; |
| |
| assert(sc); |
| sc = sc.push(tmix.argsym); |
| sc = sc.push(tmix); |
| sc.tinst = tmix; |
| sc.minst = tmix.minst; |
| for (size_t i = 0; i < tmix.members.dim; i++) |
| { |
| Dsymbol s = (*tmix.members)[i]; |
| static if (LOG) |
| { |
| printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind()); |
| } |
| s.semantic2(sc); |
| } |
| sc = sc.pop(); |
| sc.pop(); |
| } |
| |
| override void visit(VarDeclaration vd) |
| { |
| if (vd.semanticRun < PASS.semanticdone && vd.inuse) |
| return; |
| |
| //printf("VarDeclaration::semantic2('%s')\n", toChars()); |
| |
| if (vd.aliassym) // if it's a tuple |
| { |
| vd.aliassym.accept(this); |
| vd.semanticRun = PASS.semantic2done; |
| return; |
| } |
| |
| UserAttributeDeclaration.checkGNUABITag(vd, vd._linkage); |
| |
| if (vd._init && !vd.toParent().isFuncDeclaration()) |
| { |
| vd.inuse++; |
| |
| /* https://issues.dlang.org/show_bug.cgi?id=20280 |
| * |
| * Template instances may import modules that have not |
| * finished semantic1. |
| */ |
| if (!vd.type) |
| vd.dsymbolSemantic(sc); |
| |
| |
| // https://issues.dlang.org/show_bug.cgi?id=14166 |
| // https://issues.dlang.org/show_bug.cgi?id=20417 |
| // Don't run CTFE for the temporary variables inside typeof or __traits(compiles) |
| vd._init = vd._init.initializerSemantic(sc, vd.type, sc.intypeof == 1 || sc.flags & SCOPE.compile ? INITnointerpret : INITinterpret); |
| vd.inuse--; |
| } |
| if (vd._init && vd.storage_class & STC.manifest) |
| { |
| /* Cannot initializer enums with CTFE classreferences and addresses of struct literals. |
| * Scan initializer looking for them. Issue error if found. |
| */ |
| if (ExpInitializer ei = vd._init.isExpInitializer()) |
| { |
| static bool hasInvalidEnumInitializer(Expression e) |
| { |
| static bool arrayHasInvalidEnumInitializer(Expressions* elems) |
| { |
| foreach (e; *elems) |
| { |
| if (e && hasInvalidEnumInitializer(e)) |
| return true; |
| } |
| return false; |
| } |
| |
| if (e.op == EXP.classReference) |
| return true; |
| if (e.op == EXP.address && (cast(AddrExp)e).e1.op == EXP.structLiteral) |
| return true; |
| if (e.op == EXP.arrayLiteral) |
| return arrayHasInvalidEnumInitializer((cast(ArrayLiteralExp)e).elements); |
| if (e.op == EXP.structLiteral) |
| return arrayHasInvalidEnumInitializer((cast(StructLiteralExp)e).elements); |
| if (e.op == EXP.assocArrayLiteral) |
| { |
| AssocArrayLiteralExp ae = cast(AssocArrayLiteralExp)e; |
| return arrayHasInvalidEnumInitializer(ae.values) || |
| arrayHasInvalidEnumInitializer(ae.keys); |
| } |
| return false; |
| } |
| |
| if (hasInvalidEnumInitializer(ei.exp)) |
| vd.error(": Unable to initialize enum with class or pointer to struct. Use static const variable instead."); |
| } |
| } |
| else if (vd._init && vd.isThreadlocal()) |
| { |
| // Cannot initialize a thread-local class or pointer to struct variable with a literal |
| // that itself is a thread-local reference and would need dynamic initialization also. |
| if ((vd.type.ty == Tclass) && vd.type.isMutable() && !vd.type.isShared()) |
| { |
| ExpInitializer ei = vd._init.isExpInitializer(); |
| if (ei && ei.exp.op == EXP.classReference) |
| vd.error("is a thread-local class and cannot have a static initializer. Use `static this()` to initialize instead."); |
| } |
| else if (vd.type.ty == Tpointer && vd.type.nextOf().ty == Tstruct && vd.type.nextOf().isMutable() && !vd.type.nextOf().isShared()) |
| { |
| ExpInitializer ei = vd._init.isExpInitializer(); |
| if (ei && ei.exp.op == EXP.address && (cast(AddrExp)ei.exp).e1.op == EXP.structLiteral) |
| vd.error("is a thread-local pointer to struct and cannot have a static initializer. Use `static this()` to initialize instead."); |
| } |
| } |
| vd.semanticRun = PASS.semantic2done; |
| } |
| |
| override void visit(Module mod) |
| { |
| //printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent); |
| if (mod.semanticRun != PASS.semanticdone) // semantic() not completed yet - could be recursive call |
| return; |
| mod.semanticRun = PASS.semantic2; |
| // Note that modules get their own scope, from scratch. |
| // This is so regardless of where in the syntax a module |
| // gets imported, it is unaffected by context. |
| Scope* sc = Scope.createGlobal(mod); // create root scope |
| //printf("Module = %p\n", sc.scopesym); |
| if (mod.members) |
| { |
| // Pass 2 semantic routines: do initializers and function bodies |
| for (size_t i = 0; i < mod.members.dim; i++) |
| { |
| Dsymbol s = (*mod.members)[i]; |
| s.semantic2(sc); |
| } |
| } |
| if (mod.userAttribDecl) |
| { |
| mod.userAttribDecl.semantic2(sc); |
| } |
| sc = sc.pop(); |
| sc.pop(); |
| mod.semanticRun = PASS.semantic2done; |
| //printf("-Module::semantic2('%s'): parent = %p\n", toChars(), parent); |
| } |
| |
| override void visit(FuncDeclaration fd) |
| { |
| if (fd.semanticRun >= PASS.semantic2done) |
| return; |
| |
| if (fd.semanticRun < PASS.semanticdone && !fd.errors) |
| { |
| /* https://issues.dlang.org/show_bug.cgi?id=21614 |
| * |
| * Template instances may import modules that have not |
| * finished semantic1. |
| */ |
| fd.dsymbolSemantic(sc); |
| } |
| assert(fd.semanticRun <= PASS.semantic2); |
| fd.semanticRun = PASS.semantic2; |
| |
| //printf("FuncDeclaration::semantic2 [%s] fd: %s type: %s\n", fd.loc.toChars(), fd.toChars(), fd.type ? fd.type.toChars() : "".ptr); |
| |
| // Only check valid functions which have a body to avoid errors |
| // for multiple declarations, e.g. |
| // void foo(); |
| // void foo(); |
| if (fd.fbody && fd.overnext && !fd.errors) |
| { |
| // Always starts the lookup from 'this', because the conflicts with |
| // previous overloads are already reported. |
| alias f1 = fd; |
| auto tf1 = cast(TypeFunction) f1.type; |
| auto parent1 = f1.toParent2(); |
| const linkage1 = f1.resolvedLinkage(); |
| |
| overloadApply(f1, (Dsymbol s) |
| { |
| auto f2 = s.isFuncDeclaration(); |
| if (!f2 || f1 == f2 || f2.errors) |
| return 0; |
| |
| // Don't have to check conflict between declaration and definition. |
| if (f2.fbody is null) |
| return 0; |
| |
| // Functions with different manglings can never conflict |
| if (linkage1 != f2.resolvedLinkage()) |
| return 0; |
| |
| // Functions with different names never conflict |
| // (they can form overloads sets introduced by an alias) |
| if (f1.ident != f2.ident) |
| return 0; |
| |
| // Functions with different parents never conflict |
| // (E.g. when aliasing a free function into a struct) |
| if (parent1 != f2.toParent2()) |
| return 0; |
| |
| /* Check for overload merging with base class member functions. |
| * |
| * class B { void foo() {} } |
| * class D : B { |
| * override void foo() {} // B.foo appears as f2 |
| * alias foo = B.foo; |
| * } |
| */ |
| if (f1.overrides(f2)) |
| return 0; |
| |
| auto tf2 = cast(TypeFunction) f2.type; |
| |
| // Overloading based on storage classes |
| if (tf1.mod != tf2.mod || ((f1.storage_class ^ f2.storage_class) & STC.static_)) |
| return 0; |
| |
| const sameAttr = tf1.attributesEqual(tf2); |
| const sameParams = tf1.parameterList == tf2.parameterList; |
| |
| // Allow the hack to declare overloads with different parameters/STC's |
| // @@@DEPRECATED_2.104@@@ |
| // Deprecated in 2020-08, make this an error in 2.104 |
| if (parent1.isModule() && |
| linkage1 != LINK.d && linkage1 != LINK.cpp && |
| (!sameAttr || !sameParams) |
| ) |
| { |
| f2.deprecation("cannot overload `extern(%s)` function at %s", |
| linkageToChars(f1._linkage), |
| f1.loc.toChars()); |
| return 0; |
| } |
| |
| // Different parameters don't conflict in extern(C++/D) |
| if (!sameParams) |
| return 0; |
| |
| // Different attributes don't conflict in extern(D) |
| if (!sameAttr && linkage1 == LINK.d) |
| return 0; |
| |
| error(f2.loc, "%s `%s%s` conflicts with previous declaration at %s", |
| f2.kind(), |
| f2.toPrettyChars(), |
| parametersTypeToChars(tf2.parameterList), |
| f1.loc.toChars()); |
| f2.type = Type.terror; |
| f2.errors = true; |
| return 0; |
| }); |
| } |
| if (!fd.type || fd.type.ty != Tfunction) |
| return; |
| TypeFunction f = cast(TypeFunction) fd.type; |
| |
| UserAttributeDeclaration.checkGNUABITag(fd, fd._linkage); |
| //semantic for parameters' UDAs |
| foreach (i, param; f.parameterList) |
| { |
| if (param && param.userAttribDecl) |
| param.userAttribDecl.semantic2(sc); |
| } |
| } |
| |
| override void visit(Import i) |
| { |
| //printf("Import::semantic2('%s')\n", toChars()); |
| if (!i.mod) |
| return; |
| |
| i.mod.semantic2(null); |
| if (i.mod.needmoduleinfo) |
| { |
| //printf("module5 %s because of %s\n", sc._module.toChars(), mod.toChars()); |
| if (sc) |
| sc._module.needmoduleinfo = 1; |
| } |
| } |
| |
| override void visit(Nspace ns) |
| { |
| if (ns.semanticRun >= PASS.semantic2) |
| return; |
| ns.semanticRun = PASS.semantic2; |
| static if (LOG) |
| { |
| printf("+Nspace::semantic2('%s')\n", ns.toChars()); |
| scope(exit) printf("-Nspace::semantic2('%s')\n", ns.toChars()); |
| } |
| UserAttributeDeclaration.checkGNUABITag(ns, LINK.cpp); |
| if (!ns.members) |
| return; |
| |
| assert(sc); |
| sc = sc.push(ns); |
| sc.linkage = LINK.cpp; |
| foreach (s; *ns.members) |
| { |
| static if (LOG) |
| { |
| printf("\tmember '%s', kind = '%s'\n", s.toChars(), s.kind()); |
| } |
| s.semantic2(sc); |
| } |
| sc.pop(); |
| } |
| |
| override void visit(AttribDeclaration ad) |
| { |
| Dsymbols* d = ad.include(sc); |
| if (!d) |
| return; |
| |
| Scope* sc2 = ad.newScope(sc); |
| for (size_t i = 0; i < d.dim; i++) |
| { |
| Dsymbol s = (*d)[i]; |
| s.semantic2(sc2); |
| } |
| if (sc2 != sc) |
| sc2.pop(); |
| } |
| |
| /** |
| * Run the DeprecatedDeclaration's semantic2 phase then its members. |
| * |
| * The message set via a `DeprecatedDeclaration` can be either of: |
| * - a string literal |
| * - an enum |
| * - a static immutable |
| * So we need to call ctfe to resolve it. |
| * Afterward forwards to the members' semantic2. |
| */ |
| override void visit(DeprecatedDeclaration dd) |
| { |
| getMessage(dd); |
| visit(cast(AttribDeclaration)dd); |
| } |
| |
| override void visit(AlignDeclaration ad) |
| { |
| ad.getAlignment(sc); |
| visit(cast(AttribDeclaration)ad); |
| } |
| |
| override void visit(CPPNamespaceDeclaration decl) |
| { |
| UserAttributeDeclaration.checkGNUABITag(decl, LINK.cpp); |
| visit(cast(AttribDeclaration)decl); |
| } |
| |
| override void visit(UserAttributeDeclaration uad) |
| { |
| if (!uad.decl || !uad.atts || !uad.atts.dim || !uad._scope) |
| return visit(cast(AttribDeclaration)uad); |
| |
| Expression* lastTag; |
| static void eval(Scope* sc, Expressions* exps, ref Expression* lastTag) |
| { |
| foreach (ref Expression e; *exps) |
| { |
| if (!e) |
| continue; |
| |
| e = e.expressionSemantic(sc); |
| if (definitelyValueParameter(e)) |
| e = e.ctfeInterpret(); |
| if (e.op == EXP.tuple) |
| { |
| TupleExp te = cast(TupleExp)e; |
| eval(sc, te.exps, lastTag); |
| } |
| |
| // Handles compiler-recognized `core.attribute.gnuAbiTag` |
| if (UserAttributeDeclaration.isGNUABITag(e)) |
| doGNUABITagSemantic(e, lastTag); |
| } |
| } |
| |
| uad._scope = null; |
| eval(sc, uad.atts, lastTag); |
| visit(cast(AttribDeclaration)uad); |
| } |
| |
| override void visit(AggregateDeclaration ad) |
| { |
| //printf("AggregateDeclaration::semantic2(%s) type = %s, errors = %d\n", ad.toChars(), ad.type.toChars(), ad.errors); |
| if (!ad.members) |
| return; |
| |
| if (ad._scope) |
| { |
| ad.error("has forward references"); |
| return; |
| } |
| |
| UserAttributeDeclaration.checkGNUABITag( |
| ad, ad.classKind == ClassKind.cpp ? LINK.cpp : LINK.d); |
| |
| auto sc2 = ad.newScope(sc); |
| |
| ad.determineSize(ad.loc); |
| |
| for (size_t i = 0; i < ad.members.dim; i++) |
| { |
| Dsymbol s = (*ad.members)[i]; |
| //printf("\t[%d] %s\n", i, s.toChars()); |
| s.semantic2(sc2); |
| } |
| |
| sc2.pop(); |
| } |
| |
| override void visit(ClassDeclaration cd) |
| { |
| /// Checks that the given class implements all methods of its interfaces. |
| static void checkInterfaceImplementations(ClassDeclaration cd) |
| { |
| foreach (base; cd.interfaces) |
| { |
| // first entry is ClassInfo reference |
| auto methods = base.sym.vtbl[base.sym.vtblOffset .. $]; |
| |
| foreach (m; methods) |
| { |
| auto ifd = m.isFuncDeclaration; |
| assert(ifd); |
| |
| if (ifd.objc.isOptional) |
| continue; |
| |
| auto type = ifd.type.toTypeFunction(); |
| auto fd = cd.findFunc(ifd.ident, type); |
| |
| if (fd && !fd.isAbstract) |
| { |
| //printf(" found\n"); |
| // Check that calling conventions match |
| if (fd._linkage != ifd._linkage) |
| fd.error("linkage doesn't match interface function"); |
| |
| // Check that it is current |
| //printf("newinstance = %d fd.toParent() = %s ifd.toParent() = %s\n", |
| //newinstance, fd.toParent().toChars(), ifd.toParent().toChars()); |
| if (fd.toParent() != cd && ifd.toParent() == base.sym) |
| cd.error("interface function `%s` is not implemented", ifd.toFullSignature()); |
| } |
| else |
| { |
| //printf(" not found %p\n", fd); |
| // BUG: should mark this class as abstract? |
| if (!cd.isAbstract()) |
| cd.error("interface function `%s` is not implemented", ifd.toFullSignature()); |
| } |
| } |
| } |
| } |
| |
| if (cd.semanticRun >= PASS.semantic2done) |
| return; |
| assert(cd.semanticRun <= PASS.semantic2); |
| cd.semanticRun = PASS.semantic2; |
| |
| checkInterfaceImplementations(cd); |
| visit(cast(AggregateDeclaration) cd); |
| } |
| |
| override void visit(InterfaceDeclaration cd) |
| { |
| visit(cast(AggregateDeclaration) cd); |
| } |
| |
| override void visit(TupleDeclaration td) |
| { |
| td.foreachVar((s) { s.accept(this); }); |
| } |
| } |
| |
| /** |
| * Perform semantic analysis specific to the GNU ABI tags |
| * |
| * The GNU ABI tags are a feature introduced in C++11, specific to g++ |
| * and the Itanium ABI. |
| * They are mandatory for C++ interfacing, simply because the templated struct |
| *`std::basic_string`, of which the ubiquitous `std::string` is a instantiation |
| * of, uses them. |
| * |
| * Params: |
| * e = Expression to perform semantic on |
| * See `Semantic2Visitor.visit(UserAttributeDeclaration)` |
| * lastTag = When `!is null`, we already saw an ABI tag. |
| * To simplify implementation and reflection code, |
| * only one ABI tag object is allowed per symbol |
| * (but it can have multiple tags as it's an array exp). |
| */ |
| private void doGNUABITagSemantic(ref Expression e, ref Expression* lastTag) |
| { |
| import dmd.dmangle; |
| |
| // When `@gnuAbiTag` is used, the type will be the UDA, not the struct literal |
| if (e.op == EXP.type) |
| { |
| e.error("`@%s` at least one argument expected", Id.udaGNUAbiTag.toChars()); |
| return; |
| } |
| |
| // Definition is in `core.attributes`. If it's not a struct literal, |
| // it shouldn't have passed semantic, hence the `assert`. |
| auto sle = e.isStructLiteralExp(); |
| if (sle is null) |
| { |
| assert(global.errors); |
| return; |
| } |
| // The definition of `gnuAttributes` only have 1 member, `string[] tags` |
| assert(sle.elements && sle.elements.length == 1); |
| // `gnuAbiTag`'s constructor is defined as `this(string[] tags...)` |
| auto ale = (*sle.elements)[0].isArrayLiteralExp(); |
| if (ale is null) |
| { |
| e.error("`@%s` at least one argument expected", Id.udaGNUAbiTag.toChars()); |
| return; |
| } |
| |
| // Check that it's the only tag on the symbol |
| if (lastTag !is null) |
| { |
| const str1 = (*lastTag.isStructLiteralExp().elements)[0].toString(); |
| const str2 = ale.toString(); |
| e.error("only one `@%s` allowed per symbol", Id.udaGNUAbiTag.toChars()); |
| e.errorSupplemental("instead of `@%s @%s`, use `@%s(%.*s, %.*s)`", |
| lastTag.toChars(), e.toChars(), Id.udaGNUAbiTag.toChars(), |
| // Avoid [ ... ] |
| cast(int)str1.length - 2, str1.ptr + 1, |
| cast(int)str2.length - 2, str2.ptr + 1); |
| return; |
| } |
| lastTag = &e; |
| |
| // We already know we have a valid array literal of strings. |
| // Now checks that elements are valid. |
| foreach (idx, elem; *ale.elements) |
| { |
| const str = elem.toStringExp().peekString(); |
| if (!str.length) |
| { |
| e.error("argument `%d` to `@%s` cannot be %s", cast(int)(idx + 1), |
| Id.udaGNUAbiTag.toChars(), |
| elem.isNullExp() ? "`null`".ptr : "empty".ptr); |
| continue; |
| } |
| |
| foreach (c; str) |
| { |
| if (!c.isValidMangling()) |
| { |
| e.error("`@%s` char `0x%02x` not allowed in mangling", |
| Id.udaGNUAbiTag.toChars(), c); |
| break; |
| } |
| } |
| // Valid element |
| } |
| // Since ABI tags need to be sorted, we sort them in place |
| // It might be surprising for users that inspects the UDAs, |
| // but it's a concession to practicality. |
| // Casts are unfortunately necessary as `implicitConvTo` is not |
| // `const` (and nor is `StringExp`, by extension). |
| static int predicate(const scope Expression* e1, const scope Expression* e2) nothrow |
| { |
| scope(failure) assert(0, "An exception was thrown"); |
| return (cast(Expression*)e1).toStringExp().compare((cast(Expression*)e2).toStringExp()); |
| } |
| ale.elements.sort!predicate; |
| } |