diff --git a/src/class.c b/src/class.c index e2433af8762f..d569cd9a06b1 100644 --- a/src/class.c +++ b/src/class.c @@ -26,10 +26,13 @@ #include "mtype.h" #include "scope.h" #include "module.h" +#include "import.h" #include "expression.h" #include "statement.h" #include "template.h" +bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd); + /********************************* ClassDeclaration ****************************/ ClassDeclaration *ClassDeclaration::object; @@ -614,6 +617,28 @@ void ClassDeclaration::semantic(Scope *sc) size_t members_dim = members->dim; sizeok = SIZEOKnone; + // Merge base class imports in this class + for (size_t i = 0; i < baseclasses->dim; i++) + { + ClassDeclaration *cd = (*baseclasses)[i]->base; + if (cd && cd->imports) + { + for (size_t j = 0; j < cd->imports->dim; j++) + { + Import *imp = (*cd->imports)[j]->isImport(); + PROT prot = cd->prots[j]; + if (imp && (prot == PROTpublic || prot == PROTprotected)) + { + imp = imp->copy(); + imp->protection = prot; + imp->isstatic = true; // Don't insert to sc->scopesym->imports + imp->overnext = NULL; + imp->importScope(sc); + } + } + } + } + /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ @@ -949,7 +974,9 @@ Dsymbol *ClassDeclaration::search(Loc loc, Identifier *ident, int flags) error("base %s is forward referenced", b->base->ident->toChars()); else { - s = b->base->search(loc, ident, flags); + int flags2 = flags | IgnoreImportedFQN; + if (!isFriendOf(this, b->base)) flags2 |= IgnorePrivateImports; + s = b->base->search(loc, ident, flags2); if (s == this) // happens if s is nested in this and derives from this s = NULL; else if (s) diff --git a/src/dsymbol.c b/src/dsymbol.c index f54ccab769dc..e3c444e3315b 100644 --- a/src/dsymbol.c +++ b/src/dsymbol.c @@ -442,7 +442,7 @@ Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id) switch (id->dyncast()) { case DYNCAST_IDENTIFIER: - sm = s->search(loc, (Identifier *)id); + sm = s->search(loc, (Identifier *)id, IgnoreImportedFQN); break; case DYNCAST_DSYMBOL: @@ -450,7 +450,7 @@ Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id) //printf("\ttemplate instance id\n"); Dsymbol *st = (Dsymbol *)id; TemplateInstance *ti = st->isTemplateInstance(); - sm = s->search(loc, ti->name); + sm = s->search(loc, ti->name, IgnoreImportedFQN); if (!sm) { sm = s->search_correct(ti->name); @@ -856,6 +856,7 @@ ScopeDsymbol::ScopeDsymbol() symtab = NULL; imports = NULL; prots = NULL; + pkgtab = NULL; } ScopeDsymbol::ScopeDsymbol(Identifier *id) @@ -865,6 +866,7 @@ ScopeDsymbol::ScopeDsymbol(Identifier *id) symtab = NULL; imports = NULL; prots = NULL; + pkgtab = NULL; } Dsymbol *ScopeDsymbol::syntaxCopy(Dsymbol *s) @@ -895,7 +897,10 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) //printf("\ts1 = %p, imports = %p, %d\n", s1, imports, imports ? imports->dim : 0); if (s1) { - //printf("\ts = '%s.%s'\n",toChars(),s1->toChars()); + //printf("\ts = '%s.%s', prot = %d\n", toChars(), s1->toChars(), s1->prot()); + // The found symbol which has private access should be invisible + if ((flags & IgnorePrivateMembers) && !s1->isOverloadable() && s1->prot() == PROTprivate) + s1 = NULL; return s1; } @@ -908,15 +913,13 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) for (size_t i = 0; i < imports->dim; i++) { // If private import, don't search it - if ((flags & IgnorePrivateMembers) && prots[i] == PROTprivate) + if ((flags & IgnorePrivateSymbols) && prots[i] == PROTprivate) continue; Dsymbol *ss = (*imports)[i]; - //printf("\tscanning import '%s', prots = %d, isModule = %p, isImport = %p\n", ss->toChars(), prots[i], ss->isModule(), ss->isImport()); - /* Don't find private members if ss is a module - */ - Dsymbol *s2 = ss->search(loc, ident, ss->isModule() ? IgnorePrivateMembers : IgnoreNone); + //printf("\tscanning imports[%d] : %s '%s', prots = %d\n", i, ss->kind(), ss->toChars(), prots[i]); + Dsymbol *s2 = ss->search(loc, ident, flags & IgnorePrivateImports); if (!s) s = s2; else if (s2 && s != s2) @@ -1003,7 +1006,8 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) s = a; } - if (!(flags & IgnoreErrors) && s->prot() == PROTprivate && !s->parent->isTemplateMixin()) + if (!(flags & IgnoreErrors) && !s->isOverloadable() && + s->prot() == PROTprivate && !s->parent->isTemplateMixin()) { if (!s->isImport()) error(loc, "%s %s is private", s->kind(), s->toPrettyChars()); @@ -1012,6 +1016,11 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) } } + if (!(flags & IgnoreImportedFQN) && pkgtab) + { + // find leftmost package/module name for FQN access + s1 = pkgtab->lookup(ident); + } return s1; } diff --git a/src/dsymbol.h b/src/dsymbol.h index 0c0ae10400d8..4a5a1f4d8041 100644 --- a/src/dsymbol.h +++ b/src/dsymbol.h @@ -123,6 +123,10 @@ enum IgnorePrivateMembers = 0x01, // don't find private members IgnoreErrors = 0x02, // don't give error messages IgnoreAmbiguous = 0x04, // return NULL if ambiguous + IgnoreImportedFQN = 0x08, // don't find imported FQNs + IgnorePrivateImports = 0x10, // don't find privately imported symbols + + IgnorePrivateSymbols = IgnorePrivateMembers | IgnorePrivateImports, }; typedef int (*Dsymbol_apply_ft_t)(Dsymbol *, void *); @@ -279,6 +283,8 @@ class ScopeDsymbol : public Dsymbol Dsymbols *imports; // imported Dsymbol's PROT *prots; // array of PROT, one for each import + DsymbolTable *pkgtab; // accessible packages/modules from this scope + ScopeDsymbol(); ScopeDsymbol(Identifier *id); Dsymbol *syntaxCopy(Dsymbol *s); diff --git a/src/expression.c b/src/expression.c index f72c97d937fa..4ff88567913f 100644 --- a/src/expression.c +++ b/src/expression.c @@ -3262,8 +3262,8 @@ Expression *DsymbolExp::semantic(Scope *sc) error("forward reference of import %s", imp->toChars()); return new ErrorExp(); } - ScopeExp *ie = new ScopeExp(loc, imp->pkg); - return ie->semantic(sc); + type = Type::tvoid; + return this; } if (Package *pkg = s->isPackage()) { @@ -6933,6 +6933,13 @@ Expression *DotIdExp::semanticX(Scope *sc) e = e->semantic(sc); return e; } + case TOKdsymbol: + if (Import *imp = ((DsymbolExp *)e1)->s->isImport()) + { + ds = imp->mod; + goto L1; + } + break; default: break; } @@ -7041,18 +7048,54 @@ Expression *DotIdExp::semanticY(Scope *sc, int flag) Type *t1b = e1->type->toBasetype(); + ScopeExp *ie = NULL; + Dsymbol *s; + if (eright->op == TOKdsymbol && ((DsymbolExp *)eright)->s->isImport()) + { + Import *imp = (Import *)((DsymbolExp *)eright)->s; + if (imp->mod == sc->module) + s = imp->mod->search(loc, ident, IgnoreImportedFQN); + else + s = imp->search(loc, ident, IgnoreImportedFQN | IgnorePrivateMembers); + if (s) + goto L1; + else if (ident == Id::stringof) + { + OutBuffer buf; + buf.writestring(imp->mod->kind()); + buf.writestring(" "); + buf.writestring(imp->mod->toChars()); + buf.writeByte(0); + char *s = buf.extractData(); + e = new StringExp(loc, s, strlen(s), 'c'); + e = e->semantic(sc); + return e; + } + else + { + s = imp->search_correct(ident); + if (s) + error("undefined identifier '%s', did you mean '%s %s'?", + ident->toChars(), s->kind(), s->toChars()); + else + error("undefined identifier '%s'", ident->toChars()); + return new ErrorExp(); + } + } if (eright->op == TOKimport) // also used for template alias's { - ScopeExp *ie = (ScopeExp *)eright; + ie = (ScopeExp *)eright; /* Disable access to another module's private imports. * The check for 'is sds our current module' is because * the current module should have access to its own imports. */ - Dsymbol *s = ie->sds->search(loc, ident, - (ie->sds->isModule() && ie->sds != sc->module) ? IgnorePrivateMembers : IgnoreNone); + s = ie->sds->search(loc, ident, + (ie->sds->isModule() && ie->sds != sc->module ? IgnoreImportedFQN | IgnorePrivateMembers : IgnoreNone) + ); if (s) { + L1: /* Check for access before resolving aliases because public * aliases to private symbols are public. */ @@ -7158,8 +7201,8 @@ Expression *DotIdExp::semanticY(Scope *sc, int flag) Import *imp = s->isImport(); if (imp) { - ie = new ScopeExp(loc, imp->pkg); - return ie->semantic(sc); + DsymbolExp *se = new DsymbolExp(loc, imp); + return se->semantic(sc); } // BUG: handle other cases like in IdentifierExp::semantic() diff --git a/src/import.c b/src/import.c index f5db0517f191..6cde3661a695 100644 --- a/src/import.c +++ b/src/import.c @@ -23,6 +23,27 @@ #include "id.h" #include "attrib.h" +Package *findPackage(DsymbolTable *dst, Identifiers *packages, size_t dim) +{ + if (!packages || !dim) + return NULL; + assert(dim <= packages->dim); + + Package *pkg; + for (size_t i = 0; i < dim; i++) + { + assert(dst); + Identifier *pid = (*packages)[i]; + Dsymbol *p = dst->lookup(pid); + if (!p) + return NULL; + pkg = p->isPackage(); + assert(pkg); + dst = pkg->symtab; + } + return pkg; +} + /********************************* Import ****************************/ Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, @@ -50,23 +71,7 @@ Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *alias this->protection = PROTprivate; // default to private this->pkg = NULL; this->mod = NULL; - - // Set symbol name (bracketed) - if (aliasId) - { - // import [cstdio] = std.stdio; - this->ident = aliasId; - } - else if (packages && packages->dim) - { - // import [std].stdio; - this->ident = (*packages)[0]; - } - else - { - // import [foo]; - this->ident = id; - } + this->overnext = NULL; } void Import::addAlias(Identifier *name, Identifier *alias) @@ -74,9 +79,6 @@ void Import::addAlias(Identifier *name, Identifier *alias) if (isstatic) error("cannot have an import bind list"); - if (!aliasId) - this->ident = NULL; // make it an anonymous import - names.push(name); aliases.push(alias); } @@ -91,6 +93,13 @@ PROT Import::prot() return protection; } +Import *Import::copy() +{ + Import *imp = (Import *)mem.malloc(sizeof(Import)); + memcpy(imp, this, sizeof(Import)); + return imp; +} + Dsymbol *Import::syntaxCopy(Dsymbol *s) { assert(!s); @@ -191,11 +200,234 @@ void Import::importAll(Scope *sc) { mod->importAll(NULL); - if (!isstatic && !aliasId && !names.dim) + if (sc->explicitProtection) + protection = sc->protection; + importScope(sc); + } + } +} + +/******************************************** + * Add import declaration in scope local package tree. + */ +void Import::importScope(Scope *sc) +{ +#if 0 + { + OutBuffer buf; + HdrGenState hgs; + toCBuffer(&buf, &hgs); +#if _WIN32 + buf.data[buf.offset - 2] = 0; // overwrite '\r\n' +#else + buf.data[buf.offset - 1] = 0; // overwrite '\n' +#endif + printf("importScope('%s') [%s] isPackageFile = %d\n", buf.data, loc.toChars(), mod->isPackageFile); + } +#endif + + Scope *scx = sc; + for (; scx; scx = scx->enclosing) + { + if (scx->scopesym) + break; + } + if (!scx) + return; + // add 'this' to sds->imports + ScopeDsymbol *sds = scx->scopesym; + + if (!isstatic) + sds->importScope(this, protection); + + if (!sds->pkgtab) + sds->pkgtab = new DsymbolTable(); + + /* Insert the fully qualified name of imported module + * in local package tree. + */ + Package *pkg = NULL; // rightmost package + DsymbolTable *dst; // rightmost DsymbolTable + Identifier *id; // rightmost import identifier: + Dsymbol *ss = this; // local package tree stores Import instead of Module + if (aliasId) + { + dst = sds->pkgtab; + id = aliasId; // import [A] = std.stdio; + } + else + { + dst = Package::resolve(sds->pkgtab, packages, &pkg, NULL); + id = this->id; // import std.[stdio]; + + if (mod->isPackageFile) + { + Package *p = new Package(id); + p->parent = pkg; // may be NULL + p->isPkgMod = PKGmodule; + p->aliassym = this; + p->symtab = new DsymbolTable(); + ss = p; + } + } + if (dst->insert(id, ss)) + { + /* Make links from inner packages to the corresponding outer packages. + * + * import std.algorithm; + * // In here, local pkgtab have a Package 'std' (a), + * // and it contains a module 'algorithm'. + * + * class C { + * import std.range; + * // In here, local pkgtab contains a Package 'std' (b), + * // and it contains a module 'range'. + * + * void test() { + * std.algorithm.map!(a=>a*2)(...); + * // 'algorithm' doesn't exist in (b)->symtab, so (b) should have + * // a link to (a). + * } + * } + * + * After following, (b)->enclosingPkg() will return (a). + */ + if (!pkg) + return; + assert(packages); + dst = sds->pkgtab; + scx = scx->enclosing; + if (!scx) + return; + for (size_t i = 0; i < packages->dim; i++) + { + assert(dst); + Dsymbol *s = dst->lookup((*packages)[i]); + assert(s); + Package *inn_pkg = s->isPackage(); + assert(inn_pkg); + + Package *out_pkg = NULL; + for (; scx; scx = scx->enclosing) + { + if (!scx->scopesym || !scx->scopesym->pkgtab) + continue; + + out_pkg = findPackage(scx->scopesym->pkgtab, packages, i + 1); + if (!out_pkg) + continue; + + /* Importing package.d hides outer scope imports, so + * further searching is not necessary. + * + * import std.algorithm; + * class C1 { + * import std; + * // Here is 'scx->scopesym' + * // out_pkg == 'std' (isPackageMod() != NULL) + * + * class C2 { + * import std.range; + * // Here is 'this' + * // inn_pkg == 'std' (isPackageMod() == NULL) + * + * void test() { + * auto r1 = std.range.iota(10); // OK + * auto r2 = std.algorithm.map!(a=>a*2)([1,2,3]); // NG + * // std.range is accessible, but + * // std.algorithm isn't. Because identifier + * // 'algorithm' is found in std/package.d + * } + * } + * } + */ + if (out_pkg->isPackageMod()) + return; + + //printf("link out(%s:%p) to (%s:%p)\n", out_pkg->toPrettyChars(), out_pkg, inn_pkg->toPrettyChars(), inn_pkg); + inn_pkg->aliassym = out_pkg; + break; + } + + /* There's no corresponding package in enclosing scope, so + * further searching is not necessary. + */ + if (!out_pkg) + break; + + dst = inn_pkg->symtab; + } + } + else + { + Dsymbol *prev = dst->lookup(id); + assert(prev); + if (Import *imp = prev->isImport()) + { + if (imp == this) + return; + if (imp->mod == mod) + { + /* OK: + * import A = std.algorithm : find; + * import A = std.algorithm : filter; + */ + Import **pimp = &imp->overnext; + while (*pimp) + { + if (*pimp == this) + return; + pimp = &(*pimp)->overnext; + } + (*pimp) = this; + } + else + { + /* NG: + * import A = std.algorithm; + * import A = std.stdio; + */ + error("import '%s' conflicts with import '%s'", toChars(), prev->toChars()); + } + } + else + { + Package *pkg = prev->isPackage(); + assert(pkg); + //printf("[%s] pkg = %d, pkg->aliassym = %p, mod = %p, mod->isPackageFile = %d\n", loc.toChars(), pkg->isPkgMod, pkg->aliassym, mod, mod->isPackageFile); + if (pkg->isPkgMod == PKGunknown && mod->isPackageFile) + { + /* OK: + * import std.datetime; + * import std; // std/package.d + */ + pkg->isPkgMod = PKGmodule; + pkg->aliassym = this; + } + else if (pkg->isPackageMod() == mod) + { + /* OK: + * import std; // std/package.d + * import std: writeln; + */ + Import *imp = pkg->aliassym->isImport(); + assert(imp); + Import **pimp = &imp->overnext; + while (*pimp) + { + if (*pimp == this) + return; + pimp = &(*pimp)->overnext; + } + (*pimp) = this; + } + else { - if (sc->explicitProtection) - protection = sc->protection; - sc->scopesym->importScope(mod, protection); + /* NG: + * import std.stdio; + * import std = std.algorithm; + */ + error("import '%s' conflicts with package '%s'", toChars(), prev->toChars()); } } } @@ -212,12 +444,7 @@ void Import::semantic(Scope *sc) } // Load if not already done so - if (!mod) - { - load(sc); - if (mod) - mod->importAll(NULL); - } + importAll(sc); if (mod) { @@ -225,20 +452,6 @@ void Import::semantic(Scope *sc) //printf("%s imports %s\n", sc->module->toChars(), mod->toChars()); sc->module->aimports.push(mod); - if (!isstatic && !aliasId && !names.dim) - { - if (sc->explicitProtection) - protection = sc->protection; - for (Scope *scd = sc; scd; scd = scd->enclosing) - { - if (scd->scopesym) - { - scd->scopesym->importScope(mod, protection); - break; - } - } - } - mod->semantic(); if (mod->needmoduleinfo) @@ -247,34 +460,48 @@ void Import::semantic(Scope *sc) sc->module->needmoduleinfo = 1; } - sc = sc->push(mod); - /* BUG: Protection checks can't be enabled yet. The issue is - * that Dsymbol::search errors before overload resolution. - */ -#if 0 - sc->protection = protection; -#else - sc->protection = PROTpublic; -#endif - for (size_t i = 0; i < aliasdecls.dim; i++) + Dsymbols *imports = mod->ScopeDsymbol::imports; + if (!names.dim && imports) { - AliasDeclaration *ad = aliasdecls[i]; - //printf("\tImport alias semantic('%s')\n", ad->toChars()); - if (mod->search(loc, names[i])) + for (Scope *scd = sc; scd; scd = scd->enclosing) { - ad->semantic(sc); + if (!scd->scopesym) + continue; + for (size_t i = 0; i < imports->dim; i++) + { + Import *imp = (*imports)[i]->isImport(); + if (imp && mod->prots[i] == PROTpublic) + { + if (!isstatic || imp->isstatic) + { + imp = imp->copy(); + imp->loc = loc; // test + imp->protection = protection; + imp->isstatic = true; // Don't insert to sc->scopesym->imports + imp->overnext = NULL; + imp->importScope(sc); + } + } + } + break; } - else + } + + for (size_t i = 0; i < names.dim; i++) + { + //printf("\tImport alias semantic('%s')\n", ad->toChars()); + if (!mod->search(loc, names[i], IgnorePrivateSymbols)) { Dsymbol *s = mod->search_correct(names[i]); + if (s) + s = mod->search(loc, s->ident, IgnorePrivateSymbols); if (s) mod->error(loc, "import '%s' not found, did you mean '%s %s'?", names[i]->toChars(), s->kind(), s->toChars()); else mod->error(loc, "import '%s' not found", names[i]->toChars()); - ad->type = Type::terror; + //ad->type = Type::terror; // todo? } } - sc = sc->pop(); } // object self-imports itself, so skip that (Bugzilla 7547) @@ -385,39 +612,12 @@ Dsymbol *Import::toAlias() int Import::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) { - int result = 0; - - if (names.dim == 0) - return Dsymbol::addMember(sc, sd, memnum); - - if (aliasId) - result = Dsymbol::addMember(sc, sd, memnum); - - /* Instead of adding the import to sd's symbol table, - * add each of the alias=name pairs - */ - for (size_t i = 0; i < names.dim; i++) - { - Identifier *name = names[i]; - Identifier *alias = aliases[i]; - - if (!alias) - alias = name; - - TypeIdentifier *tname = new TypeIdentifier(loc, name); - AliasDeclaration *ad = new AliasDeclaration(loc, alias, tname); - ad->import = this; - result |= ad->addMember(sc, sd, memnum); - - aliasdecls.push(ad); - } - - return result; + return 1; } Dsymbol *Import::search(Loc loc, Identifier *ident, int flags) { - //printf("%s.Import::search(ident = '%s', flags = x%x)\n", toChars(), ident->toChars(), flags); + //printf("[%s].Import::search(ident = '%s', flags = x%x)\n", loc.toChars(), ident->toChars(), flags); if (!pkg) { @@ -426,21 +626,45 @@ Dsymbol *Import::search(Loc loc, Identifier *ident, int flags) mod->semantic(); } - // Forward it to the package/module - return pkg->search(loc, ident, flags); + // Don't find private members and import declarations + flags |= (IgnorePrivateMembers | IgnoreImportedFQN); + + Dsymbol *s = NULL; + + if (protection == PROTprivate && (flags & IgnorePrivateImports)) + { + //printf("\t-->! supress\n"); + } + else if (names.dim) + { + for (size_t i = 0; i < names.dim; i++) + { + Identifier *name = names[i]; + Identifier *alias = aliases[i]; + if ((alias ? alias : name) == ident) + { + // Forward it to the module + s = mod->search(loc, name, flags | IgnorePrivateImports); + break; + } + } + } + else + { + // Forward it to the module + s = mod->search(loc, ident, flags | IgnorePrivateImports); + } + if (!s && overnext) + { + s = overnext->search(loc, ident, flags); + } + + return s; } -bool Import::overloadInsert(Dsymbol *s) +char *Import::toChars() { - /* Allow multiple imports with the same package base, but disallow - * alias collisions (Bugzilla 5412). - */ - assert(ident && ident == s->ident); - Import *imp; - if (!aliasId && (imp = s->isImport()) != NULL && !imp->aliasId) - return true; - else - return false; + return (char *)id->toChars(); } void Import::toCBuffer(OutBuffer *buf, HdrGenState *hgs) diff --git a/src/import.h b/src/import.h index 6701c83507b5..5f7f781f11a4 100644 --- a/src/import.h +++ b/src/import.h @@ -42,26 +42,26 @@ class Import : public Dsymbol Identifiers names; Identifiers aliases; - Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, - int isstatic); - void addAlias(Identifier *name, Identifier *alias); - - AliasDeclarations aliasdecls; // corresponding AliasDeclarations for alias=name pairs - Module *mod; Package *pkg; // leftmost package/module + Import *overnext; + Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, + int isstatic); + void addAlias(Identifier *name, Identifier *alias); const char *kind(); PROT prot(); + Import *copy(); Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees void load(Scope *sc); void importAll(Scope *sc); + void importScope(Scope *sc); void semantic(Scope *sc); void semantic2(Scope *sc); Dsymbol *toAlias(); int addMember(Scope *sc, ScopeDsymbol *s, int memnum); Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); - bool overloadInsert(Dsymbol *s); + char *toChars(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Import *isImport() { return this; } diff --git a/src/module.c b/src/module.c index 1c623d968354..8234fa99708d 100644 --- a/src/module.c +++ b/src/module.c @@ -534,8 +534,10 @@ void Module::parse() */ this->ident = md->id; this->safe = md->safe; + Package *pparent = NULL; Package *ppack = NULL; - dst = Package::resolve(md->packages, &this->parent, &ppack); + dst = Package::resolve(md->packages, &pparent, &ppack); + this->parent = pparent; assert(dst); Module *m = ppack ? ppack->isModule() : NULL; @@ -582,7 +584,7 @@ void Module::parse() Package *p = new Package(ident); p->parent = this->parent; p->isPkgMod = PKGmodule; - p->mod = this; + p->aliassym = this; p->symtab = new DsymbolTable(); s = p; } @@ -610,7 +612,7 @@ void Module::parse() * link it to the actual module. */ pkg->isPkgMod = PKGmodule; - pkg->mod = this; + pkg->aliassym = this; } else error(pkg->loc, "from file %s conflicts with package name %s", @@ -646,6 +648,27 @@ void Module::importAll(Scope *prevsc) */ Scope *sc = Scope::createGlobal(this); // create root scope + if (md) + { + /* Insert self module FQN to local package tree. + * + * module p1.p2.mod; + * void foo() {} + * void main() { p1.p2.mod.foo(); } + * // access current module by FQN + */ + + Import *imp = new Import(Loc(this, 1, 1), md->packages, ident, NULL, 1); + imp->mod = this; + + if (!pkgtab) + pkgtab = new DsymbolTable(); + Package::resolve(pkgtab, md->packages, NULL, &imp->pkg); + if (!imp->pkg) + imp->pkg = this; + imp->importScope(sc); + } + // Add import of "object", even for the "object" module. // If it isn't there, some compiler rewrites, like // classinst == classinst -> .object.opEquals(classinst, classinst) @@ -1070,7 +1093,7 @@ Package::Package(Identifier *ident) : ScopeDsymbol(ident) { this->isPkgMod = PKGunknown; - this->mod = NULL; + this->aliassym = NULL; } @@ -1083,13 +1106,24 @@ Module *Package::isPackageMod() { if (isPkgMod == PKGmodule) { - return mod; + if (Module *mod = aliassym->isModule()) + return mod; + if (Import *imp = aliassym->isImport()) + return imp->mod; } return NULL; } +Package *Package::enclosingPkg() +{ + if (isPkgMod != PKGmodule && aliassym) + return aliassym->isPackage(); + return NULL; +} + /**************************************************** * Input: + * dst package tree root * packages[] the pkg1.pkg2 of pkg1.pkg2.mod * Returns: * the symbol table that mod should be inserted into @@ -1098,10 +1132,14 @@ Module *Package::isPackageMod() * *ppkg the leftmost package, i.e. pkg1, or NULL if no packages */ -DsymbolTable *Package::resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg) +DsymbolTable *Package::resolve(Identifiers *packages, Package **pparent, Package **ppkg) { - DsymbolTable *dst = Module::modules; - Dsymbol *parent = NULL; + return Package::resolve(Module::modules, packages, pparent, ppkg); +} + +DsymbolTable *Package::resolve(DsymbolTable *dst, Identifiers *packages, Package **pparent, Package **ppkg) +{ + Package *parent = NULL; //printf("Package::resolve()\n"); if (ppkg) @@ -1154,17 +1192,38 @@ DsymbolTable *Package::resolve(Identifiers *packages, Dsymbol **pparent, Package Dsymbol *Package::search(Loc loc, Identifier *ident, int flags) { - if (!isModule() && mod) + //printf("%s->Package::search(ident='%s', flags=x%x)\n", toChars(), ident->toChars(), flags); + + assert(!imports); + assert(!isModule()); + + if (isPkgMod == PKGmodule) { - // Prefer full package name. - Dsymbol *s = symtab ? symtab->lookup(ident) : NULL; + /* Prefer symbols declared in package.d. + * + * import std.algorithm; + * import std; // std/package.d + * void main() { + * map!(a=>a*2)([1,2,3]); + * std.map!(a=>a*2)([1,2,3]); + * std.algorithm.map!(a=>a*2)([1,2,3]); + * // iff std/package.d doesn't have a symbol named "algorithm", + * // std/algorithm.d would hit. + * } + */ + Dsymbol *s = aliassym->search(loc, ident, flags); if (s) return s; - //printf("[%s] through pkdmod: %s\n", loc.toChars(), toChars()); - return mod->search(loc, ident, flags); } - return ScopeDsymbol::search(loc, ident, flags); + Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); + if (!s && !isPackageMod()) + { + if (Package *pkg = enclosingPkg()) + s = pkg->search(loc, ident, flags); + } + + return s; } /* =========================== ===================== */ diff --git a/src/module.h b/src/module.h index 9cb7a6144d48..fd0b332f2d94 100644 --- a/src/module.h +++ b/src/module.h @@ -43,20 +43,23 @@ class Package : public ScopeDsymbol { public: PKG isPkgMod; - Module *mod; // != NULL if isPkgMod == PKGmodule + Dsymbol *aliassym; // isPkgMod == PKGmodule: Module/Import object corresponding to 'package.d' + // isPkgMod != PKGmodule: Package object in enclosing scope Package(Identifier *ident); const char *kind(); - static DsymbolTable *resolve(Identifiers *packages, Dsymbol **pparent, Package **ppkg); + static DsymbolTable *resolve(Identifiers *packages, Package **pparent, Package **ppkg); + static DsymbolTable *resolve(DsymbolTable *dst, Identifiers *packages, Package **pparent, Package **ppkg); Package *isPackage() { return this; } - virtual void semantic(Scope *) { } + void semantic(Scope *sc) { } Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); void accept(Visitor *v) { v->visit(this); } Module *isPackageMod(); + Package *enclosingPkg(); }; class Module : public Package diff --git a/src/mtype.c b/src/mtype.c index 7b82c38aa805..e25b6c260793 100644 --- a/src/mtype.c +++ b/src/mtype.c @@ -6412,7 +6412,7 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, sm = t->toDsymbol(sc); if (sm && id->dyncast() == DYNCAST_IDENTIFIER) { - sm = sm->search(loc, (Identifier *)id); + sm = sm->search(loc, (Identifier *)id, IgnoreImportedFQN); if (sm) goto L2; } @@ -6989,6 +6989,12 @@ void TypeTypeof::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol goto Lerr; } } + else if (exp->op == TOKdsymbol && ((DsymbolExp *)exp)->s->isImport()) + { + Import *imp = ((DsymbolExp *)exp)->s->isImport(); + error(loc, "%s %s has no type", imp->mod->kind(), imp->toChars()); + goto Lerr; + } t = exp->type; if (!t) { @@ -7839,7 +7845,7 @@ Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int } } - s = sym->search(e->loc, ident); + s = sym->search(e->loc, ident, IgnoreImportedFQN); L1: if (!s) { @@ -8378,7 +8384,7 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f return e; } - s = sym->search(e->loc, ident); + s = sym->search(e->loc, ident, IgnoreImportedFQN); L1: if (!s) { diff --git a/src/statement.c b/src/statement.c index 0c8e68e0496e..bb576096b933 100644 --- a/src/statement.c +++ b/src/statement.c @@ -29,6 +29,7 @@ #include "parse.h" #include "template.h" #include "attrib.h" +#include "module.h" #include "import.h" bool walkPostorder(Statement *s, StoppableVisitor *v); @@ -4792,32 +4793,9 @@ Statement *ImportStatement::semantic(Scope *sc) { Import *s = (*imports)[i]->isImport(); - if (!s->aliasdecls.dim) - { - for (size_t j = 0; j < s->names.dim; j++) - { - Identifier *name = s->names[j]; - Identifier *alias = s->aliases[j]; - - if (!alias) - alias = name; - - TypeIdentifier *tname = new TypeIdentifier(s->loc, name); - AliasDeclaration *ad = new AliasDeclaration(s->loc, alias, tname); - ad->import = s; - - s->aliasdecls.push(ad); - } - } - s->semantic(sc); s->semantic2(sc); sc->insert(s); - - for (size_t j = 0; j < s->aliasdecls.dim; j++) - { - sc->insert(s->aliasdecls[j]); - } } return this; } diff --git a/src/template.c b/src/template.c index bbe1fcc1443a..fef7a3ed76b7 100644 --- a/src/template.c +++ b/src/template.c @@ -24,6 +24,7 @@ #include "expression.h" #include "scope.h" #include "module.h" +#include "import.h" #include "aggregate.h" #include "declaration.h" #include "dsymbol.h" @@ -6544,6 +6545,12 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f sa = ((ScopeExp *)ea)->sds; goto Ldsym; } + if (ea->op == TOKdsymbol) + { + sa = ((DsymbolExp *)ea)->s->isImport(); + assert(sa); + goto Ldsym; + } if (ea->op == TOKfunction) { FuncExp *fe = (FuncExp *)ea; @@ -6587,6 +6594,11 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f { Ldsym: //printf("dsym %s %s\n", sa->kind(), sa->toChars()); + if (Import *imp = sa->isImport()) + { + if (sc->module == imp->mod) + sa = imp->mod; + } TupleDeclaration *d = sa->toAlias()->isTupleDeclaration(); if (d) { diff --git a/src/traits.c b/src/traits.c index 26e9631c9f68..e7880d6b0d80 100644 --- a/src/traits.c +++ b/src/traits.c @@ -404,6 +404,9 @@ Expression *semanticTraits(TraitsExp *e, Scope *sc) Dsymbol *s = getDsymbol(o); if (s) { + //printf("s = %s %s\n", s->kind(), s->toChars()); + if (Import *imp = s->isImport()) + s = imp->mod; if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943 s = fd->toAliasFunc(); if (!s->isImport()) // Bugzilla 8922 diff --git a/test/compilable/extra-files/pkg_import3a/mod.d b/test/compilable/extra-files/pkg_import3a/mod.d new file mode 100644 index 000000000000..ea7fd16f4822 --- /dev/null +++ b/test/compilable/extra-files/pkg_import3a/mod.d @@ -0,0 +1,3 @@ +module pkg_import3a.mod; + +int bar() { return 3; } diff --git a/test/compilable/extra-files/pkg_import3a/package.d b/test/compilable/extra-files/pkg_import3a/package.d new file mode 100644 index 000000000000..6c3e684defb6 --- /dev/null +++ b/test/compilable/extra-files/pkg_import3a/package.d @@ -0,0 +1,4 @@ +module pkg_import3a; + +int foo() { return 1; } +int bar() { return 2; } diff --git a/test/compilable/extra-files/pkg_import3b/mod.d b/test/compilable/extra-files/pkg_import3b/mod.d new file mode 100644 index 000000000000..6449a12bfad5 --- /dev/null +++ b/test/compilable/extra-files/pkg_import3b/mod.d @@ -0,0 +1,3 @@ +module pkg_import3b.mod; + +int bar() { return 3; } diff --git a/test/compilable/extra-files/pkg_import3b/package.d b/test/compilable/extra-files/pkg_import3b/package.d new file mode 100644 index 000000000000..252ffe6c3392 --- /dev/null +++ b/test/compilable/extra-files/pkg_import3b/package.d @@ -0,0 +1,6 @@ +module pkg_import3b; + +int foo() { return 1; } +int bar() { return 2; } + +int mod; diff --git a/test/compilable/imports/imp1a.d b/test/compilable/imports/imp1a.d new file mode 100644 index 000000000000..3da20f84587c --- /dev/null +++ b/test/compilable/imports/imp1a.d @@ -0,0 +1,3 @@ +module imports.imp1a; + +auto foo() { return 1; } diff --git a/test/compilable/imports/imp1b.d b/test/compilable/imports/imp1b.d new file mode 100644 index 000000000000..8f6b131437f2 --- /dev/null +++ b/test/compilable/imports/imp1b.d @@ -0,0 +1,3 @@ +module imports.imp1b; + +auto bar() { return 2; } diff --git a/test/compilable/imports/imp2a.d b/test/compilable/imports/imp2a.d new file mode 100644 index 000000000000..6db731418e42 --- /dev/null +++ b/test/compilable/imports/imp2a.d @@ -0,0 +1,7 @@ +module imports.imp2a; + +void foo() {} + +import imports.imp2b; + +public import imports.imp2c; diff --git a/test/compilable/imports/imp2b.d b/test/compilable/imports/imp2b.d new file mode 100644 index 000000000000..8131a8a2f220 --- /dev/null +++ b/test/compilable/imports/imp2b.d @@ -0,0 +1,3 @@ +module imports.imp2b; + +void bar() {} diff --git a/test/compilable/imports/imp2c.d b/test/compilable/imports/imp2c.d new file mode 100644 index 000000000000..e33ffb1ff823 --- /dev/null +++ b/test/compilable/imports/imp2c.d @@ -0,0 +1,3 @@ +module imports.imp2c; + +void baz() {} diff --git a/test/compilable/imports/imp4array.d b/test/compilable/imports/imp4array.d new file mode 100644 index 000000000000..d33b651595a7 --- /dev/null +++ b/test/compilable/imports/imp4array.d @@ -0,0 +1,4 @@ +auto splitter(C)(C[] s) if (is(C : char)) {} + +void join() {} +void split() {} diff --git a/test/compilable/imports/imp4range.d b/test/compilable/imports/imp4range.d new file mode 100644 index 000000000000..62feb88c4c8e --- /dev/null +++ b/test/compilable/imports/imp4range.d @@ -0,0 +1 @@ +public import imports.imp4array; \ No newline at end of file diff --git a/test/compilable/imports/imp4string.d b/test/compilable/imports/imp4string.d new file mode 100644 index 000000000000..621e556cb58f --- /dev/null +++ b/test/compilable/imports/imp4string.d @@ -0,0 +1,2 @@ +import imports.imp4range; +public import imports.imp4array : join, split; diff --git a/test/compilable/testimport1.d b/test/compilable/testimport1.d new file mode 100644 index 000000000000..5c0f6095ac36 --- /dev/null +++ b/test/compilable/testimport1.d @@ -0,0 +1,41 @@ +// PERMUTE_ARGS: + +module testimport1; + +import imports.imp1a; + +class C +{ + import imports.imp1b; + + void test() + { + imports.imp1b.bar(); + imports.imp1a.foo(); + } +} + +int global; + +void main() +{ + // From here, 1a is visible but 1b isn't. + imports.imp1a.foo(); + static assert(!__traits(compiles, imports.imp1b.bar())); + + testimport1.C c; + auto y1 = testimport1.global; + + // A declaration always hide same name root of package hierarchy. + { + int imports; + static assert(!__traits(compiles, imports.imp1a.foo())); + } + + // FQN access with Module Scope Operator works + .imports.imp1a.foo(); + auto y2 = .testimport1.global; + + // FQN access through class is not allowed + static assert(!__traits(compiles, { C.imports.imp1b.bar(); })); +} diff --git a/test/compilable/testimport2.d b/test/compilable/testimport2.d new file mode 100644 index 000000000000..a3acf772b681 --- /dev/null +++ b/test/compilable/testimport2.d @@ -0,0 +1,50 @@ +// REQUIRED_ARGS: -Icompilable/extra-files +// PERMUTE_ARGS: +// EXTRA_SOURCE: imports/imp2a.d +// EXTRA_SOURCE: imports/imp2b.d +// EXTRA_SOURCE: imports/imp2c.d + +import imports.imp2a; + +void main() +{ + // public symbols which directly imported are visible + foo(); + imports.imp2a.foo(); // by FQN + { + alias A = imports.imp2a; + A.foo(); + static assert(!__traits(compiles, A.bar())); + A.baz(); + } + + // public symbols through private import are invisible + static assert(!__traits(compiles, bar())); + static assert(!__traits(compiles, imports.imp2b.bar())); + // FQN of privately imported module is invisible + static assert(!__traits(compiles, imports.imp2b.stringof)); + { + static assert(!__traits(compiles, { alias B = imports.imp2b; })); + } + + // public symbols which indirectly imported through public import are visible + baz(); + imports.imp2c.baz(); // by FQN + // FQN of publicly imported module is visible + static assert(imports.imp2c.stringof == "module imp2c"); + { + alias C = imports.imp2c; + static assert(!__traits(compiles, C.foo())); + static assert(!__traits(compiles, C.bar())); + C.baz(); + } + + // Import Declaration itself should not have FQN + static assert(!__traits(compiles, imports.imp2a.imports.imp2b.bar())); + static assert(!__traits(compiles, imports.imp2a.imports.imp2c.baz())); + + // Applying Module Scope Operator to package/module FQN + .imports.imp2a.foo(); + static assert(!__traits(compiles, .imports.imp2b.bar())); + .imports.imp2c.baz(); +} diff --git a/test/compilable/testimport3.d b/test/compilable/testimport3.d new file mode 100644 index 000000000000..f1d15c4f028e --- /dev/null +++ b/test/compilable/testimport3.d @@ -0,0 +1,123 @@ +// REQUIRED_ARGS: -Icompilable/extra-files +// PERMUTE_ARGS: +// EXTRA_SOURCE: extra-files/pkg_import3a/package.d +// EXTRA_SOURCE: extra-files/pkg_import3a/mod.d + +// Test1 +class Test1A +{ + import pkg_import3a; // foo()==1, bar()==2 + class C + { + import pkg_import3a.mod; // bar()==3 + void test() + { + assert(foo() == 1); // OK, foo in pkg_import3a + assert(bar() == 3); // OK, bar in pkg_import3a.mod + + static assert(!__traits(compiles, pkg_import3a.foo())); + static assert(!__traits(compiles, pkg_import3a.bar())); + // --> both NG, inner sub-module import hides outer package.d FQN + + assert(pkg_import3a.mod.bar() == 3); // OK, bar in pkg_import3a.mod + } + } +} +class Test1B +{ + class C + { + import pkg_import3a; // foo()==1, bar()==2 + import pkg_import3a.mod; // bar()==3 + void test() + { + assert(foo() == 1); // OK, foo in pkg_import3a + static assert(!__traits(compiles, bar())); + // --> NG, ambiguous: pkg_import3a.bar and pkg_import3a.mod.bar + + assert(pkg_import3a.foo() == 1); // OK + assert(pkg_import3a.bar() == 2); // OK + + assert(pkg_import3a.mod.bar() == 3); // OK + } + } +} +class Test1C +{ + import pkg_import3a.mod; // bar()==3 + class C + { + import pkg_import3a; // foo()==1, bar()==2 + void test() + { + assert(foo() == 1); // OK, foo in pkg_import3a + assert(bar() == 2); // OK, bar in pkg_import3a + + assert(pkg_import3a.foo() == 1); // OK + assert(pkg_import3a.bar() == 2); // OK + + static assert(!__traits(compiles, pkg_import3a.mod.bar())); + // --> NG, package.d import 'import pkg_import3a' hides FQN 'pkg_import3a.mod' + } + } +} + +// Test2 symbol name in pkg_import3b/package.d conflicts with sibling module name which under the pkg_import3b. +class Test2A +{ + import pkg_import3b; // foo()==1, bar()==2, int mod; + class C + { + import pkg_import3b.mod; // bar()==3 + void test() + { + assert(foo() == 1); // OK, foo in pkg_import3b + assert(bar() == 3); // OK, bar in pkg_import3b.mod + + static assert(!__traits(compiles, pkg_import3b.foo())); + static assert(!__traits(compiles, pkg_import3b.bar())); + // --> both NG, inner sub-module import hides outer package.d FQN + + assert(pkg_import3b.mod.bar() == 3); // OK, bar in pkg_import3b.mod + } + } +} +class Test2B +{ + class C + { + import pkg_import3b; // foo()==1, bar()==2, int mod; + import pkg_import3b.mod; // bar()==3 + void test() + { + assert(foo() == 1); // OK, foo in pkg_import3b + static assert(!__traits(compiles, bar())); + // --> NG, ambiguous: pkg_import3b.bar and pkg_import3b.mod.bar + + assert(pkg_import3b.foo() == 1); // OK + assert(pkg_import3b.bar() == 2); // OK + + static assert(!__traits(compiles, pkg_import3b.mod.bar())); + // --> NG, pkg_import3b.mod is int variable in pkg_import3b/package.d + } + } +} +class Test2C +{ + import pkg_import3b.mod; // bar()==3 + class C + { + import pkg_import3b; // foo()==1, bar()==2, int mod; + void test() + { + assert(foo() == 1); // OK, foo in pkg_import3b + assert(bar() == 2); // OK, bar in pkg_import3b + + assert(pkg_import3b.foo() == 1); // OK + assert(pkg_import3b.bar() == 2); // OK + + static assert(!__traits(compiles, pkg_import3b.mod.bar())); + // --> NG, pkg_import3b.mod is int variable in pkg_import3b/package.d + } + } +} diff --git a/test/compilable/testimport4.d b/test/compilable/testimport4.d new file mode 100644 index 000000000000..e1836c9f9577 --- /dev/null +++ b/test/compilable/testimport4.d @@ -0,0 +1,11 @@ +auto splitter(R, Sep)(R r, Sep s) +{ +} + +void main() +{ + import imports.imp4string; + + string input, sep; + splitter(input, sep); +} diff --git a/test/d_do_test.d b/test/d_do_test.d index 92e662a12173..d332eddb83fa 100755 --- a/test/d_do_test.d +++ b/test/d_do_test.d @@ -262,6 +262,8 @@ string genTempFilename(string result_path) int system(string command) { + import std.c.process; + if (!command) return std.c.process.system(null); const commandz = toStringz(command); auto status = std.c.process.system(commandz); diff --git a/test/fail_compilation/ice12158.d b/test/fail_compilation/ice12158.d index 7b8d38d4cecb..a76f04d3f28f 100644 --- a/test/fail_compilation/ice12158.d +++ b/test/fail_compilation/ice12158.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/ice12158.d(7): Error: module object import 'nonexisting' not found +fail_compilation/ice12158.d(8): Error: module object import 'nonexisting' not found +fail_compilation/ice12158.d(9): Error: undefined identifier nonexisting --- */ import object : nonexisting;