Skip to content

Commit

Permalink
Merge pull request #4464 from 9rnsr/fix14198
Browse files Browse the repository at this point in the history
[REG2.067a] Issue 14198 - Link failure with Variant
  • Loading branch information
WalterBright committed Mar 10, 2015
2 parents 942dffe + 477f49f commit f22d9db
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 14 deletions.
53 changes: 42 additions & 11 deletions src/template.c
Expand Up @@ -5760,6 +5760,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
!semanticTiargs(sc) ||
!findBestMatch(sc, fargs))
{
Lerror:
if (gagged)
{
// Bugzilla 13220: Rollback status for later semantic re-running.
Expand All @@ -5773,6 +5774,17 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration();
assert(tempdecl);

// If tempdecl is a mixin, disallow it
if (tempdecl->ismixin)
{
error("mixin templates are not regular templates");
goto Lerror;
}

hasNestedArgs(tiargs, tempdecl->isstatic);
if (errors)
goto Lerror;

if (Module *m = tempdecl->scope->module) // should use getModule() instead?
{
// Generate these functions as they may be used
Expand All @@ -5783,12 +5795,6 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
toModuleUnittest(m);
}

// If tempdecl is a mixin, disallow it
if (tempdecl->ismixin)
error("mixin templates are not regular templates");

hasNestedArgs(tiargs, tempdecl->isstatic);

/* See if there is an existing TemplateInstantiation that already
* implements the typeargs. If so, just refer to that one instead.
*/
Expand Down Expand Up @@ -6095,11 +6101,8 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
if (global.errors != errorsave)
goto Laftersemantic;

if (sc->func && (aliasdecl && aliasdecl->toAlias()->isFuncDeclaration() || !tinst))
if (sc->func && !tinst)
{
/* Template function instantiation should run semantic3 immediately
* for attribute inference.
*/
/* If a template is instantiated inside function, the whole instantiation
* should be done at that position. But, immediate running semantic3 of
* dependent templates may cause unresolved forward reference (Bugzilla 9050).
Expand All @@ -6121,6 +6124,14 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs)
}
else if (tinst)
{
/* Template function instantiation should run semantic3 immediately
* for attribute inference.
*/
if (sc->func && aliasdecl && aliasdecl->toAlias()->isFuncDeclaration())
{
trySemantic3(sc2);
}

TemplateInstance *ti = tinst;
int nest = 0;
while (ti && !ti->deferred && ti->tinst)
Expand Down Expand Up @@ -7081,6 +7092,7 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic)
ea->op != TOKstructliteral)
{
ea->error("expression %s is not a valid template value argument", ea->toChars());
errors = true;
}
}
else if (sa)
Expand Down Expand Up @@ -7130,13 +7142,17 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic)
}
error("%s is nested in both %s and %s",
toChars(), enclosing->toChars(), dparent->toChars());
errors = true;
}
L1:
//printf("\tnested inside %s\n", enclosing->toChars());
nested |= 1;
}
else
{
error("cannot use local '%s' as parameter to non-global template %s", sa->toChars(), tempdecl->toChars());
errors = true;
}
}
}
else if (va)
Expand Down Expand Up @@ -7736,18 +7752,33 @@ bool TemplateInstance::needsCodegen()
{
minst = tinst->minst; // cache result
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}
if (tnext && tnext->needsCodegen())
{
minst = tnext->minst; // cache result
assert(minst);
assert(minst->isRoot() || minst->rootImports());
return true;
}
return false;
}

if (!minst->isRoot())
if (minst->isRoot())
{
// Prefer instantiation in non-root module, to minimize object code size
TemplateInstance *tnext = this->tnext;
this->tnext = NULL;

if (tnext && !tnext->needsCodegen() && tnext->minst)
{
minst = tnext->minst; // cache result
assert(!minst->isRoot());
return false;
}
}
else
{
/* If a TemplateInstance is ever instantiated by non-root modules,
* we do not have to generate code for it,
Expand Down
4 changes: 2 additions & 2 deletions src/toobj.c
Expand Up @@ -1100,7 +1100,7 @@ void toObjFile(Dsymbol *ds, bool multiobj)
void visit(TemplateInstance *ti)
{
#if LOG
printf("TemplateInstance::toObjFile('%s', this = %p)\n", ti->toChars(), ti);
printf("TemplateInstance::toObjFile(%p, '%s')\n", ti, ti->toChars());
#endif
if (!isError(ti) && ti->members)
{
Expand All @@ -1109,7 +1109,7 @@ void toObjFile(Dsymbol *ds, bool multiobj)
//printf("-speculative (%p, %s)\n", ti, ti->toPrettyChars());
return;
}
//printf("TemplateInstance::toObjFile('%s', this = %p)\n", ti->toChars(), this);
//printf("TemplateInstance::toObjFile(%p, '%s')\n", ti, ti->toPrettyChars());

if (multiobj)
{
Expand Down
2 changes: 1 addition & 1 deletion test/fail_compilation/fail12378.d
Expand Up @@ -52,10 +52,10 @@ fail_compilation/fail12378.d(63): instantiated from here: mapI!(Result)
fail_compilation/fail12378.d(143): instantiated from here: __lambda1!int
fail_compilation/fail12378.d(135): instantiated from here: MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result)
fail_compilation/fail12378.d(62): instantiated from here: mapI!(Result)
fail_compilation/fail12378.d(143): Error: static function fail12378.testI.MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result).MapResultI.front cannot access frame of function fail12378.testI
---
*/


void testI()
{
auto r =
Expand Down
27 changes: 27 additions & 0 deletions test/runnable/extra-files/std14198/array.d
@@ -0,0 +1,27 @@
module std14198.array;

version(bug14198)
{
}
else
import std14198.uni;

struct Appender(A)
{
alias T = immutable char;

void put(U)(U item)
if (
is(U : T) ||
is(immutable U == immutable char)
)
{
import std14198.uni; // necessary
assert(0);
}
}

Appender!A appender(A)()
{
return Appender!A();
}
34 changes: 34 additions & 0 deletions test/runnable/extra-files/std14198/conv.d
@@ -0,0 +1,34 @@
module std14198.conv;

template to(T)
{
T to(A...)(A args)
{
return toImpl!T(args);
}
}

T toImpl(T, S)(S value)
if (is(S : T))
{
return value;
}

T toImpl(T, S)(S value)
if (!is(S : T) &&
is(T == string))
{
alias src = value;

import std14198.format : FormatSpec;
import std14198.array : appender;

auto w = appender!T();
FormatSpec!char f; // necessary

string str = src ? "true" : "false";
for (; !str.length; str = str[1..$])
w.put(str[0]);

return "";
}
11 changes: 11 additions & 0 deletions test/runnable/extra-files/std14198/format.d
@@ -0,0 +1,11 @@
module std14198.format;

struct FormatSpec(Char) if (is(Char == char))
{
import std14198.conv;

string toString()
{
return to!string(true); // necessary
}
}
12 changes: 12 additions & 0 deletions test/runnable/extra-files/std14198/uni.d
@@ -0,0 +1,12 @@
module std14198.uni;

alias CodepointSet = InversionList!();

struct InversionList()
{
import std14198.format;

void toString(FormatSpec!char fs) // necessary
{
}
}
27 changes: 27 additions & 0 deletions test/runnable/extra-files/test14198.d
@@ -0,0 +1,27 @@
module test14198;

import std14198.conv;

struct S
{
ptrdiff_t function() fptr = &handler;

static ptrdiff_t handler() pure @safe
{
static if (is(typeof(to!string(false))))
{
to!string(false);
// [1] to!string(bool src) should be deduced to pure @safe, and the function will be mangled to:
// --> _D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya
// [2] its object code should be stored in the library file, because it's instantiated in std14188.uni:
// --> FormatSpec!char --> to!string(bool src) in FormatSpec!char.toString()
}
else
static assert(0);
return 0;
}
}

void main()
{
}
24 changes: 24 additions & 0 deletions test/runnable/link14198a.sh
@@ -0,0 +1,24 @@
#!/usr/bin/env bash

src=runnable${SEP}extra-files
dir=${RESULTS_DIR}${SEP}runnable
output_file=${dir}/link14198a.sh.out

if [ $OS == "win32" -o $OS == "win64" ]; then
LIBEXT=.lib
else
LIBEXT=.a
fi
libname=${dir}${SEP}lib14198a${LIBEXT}

# build library
$DMD -m${MODEL} -I${src} -of${libname} -lib ${src}${SEP}std14198${SEP}array.d ${src}${SEP}std14198${SEP}conv.d ${src}${SEP}std14198${SEP}format.d ${src}${SEP}std14198${SEP}uni.d || exit 1

# Do not link failure with library file, regardless the semantic order.
$DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198a${EXE} ${src}${SEP}test14198.d ${libname} || exit 1
$DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198a${EXE} -version=bug14198 ${src}${SEP}test14198.d ${libname} || exit 1

rm ${libname}
rm ${dir}/{test14198a${OBJ},test14198a${EXE}}

echo Success > ${output_file}
26 changes: 26 additions & 0 deletions test/runnable/link14198b.sh
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

src=runnable${SEP}extra-files
dir=${RESULTS_DIR}${SEP}runnable
output_file=${dir}/link14198b.sh.out

rm -f ${output_file}

if [ $OS == "win32" -o $OS == "win64" ]; then
LIBEXT=.lib
else
LIBEXT=.a
fi
libname=${dir}${SEP}lib14198b${LIBEXT}

# Do link failure without library file.

$DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198b${EXE} ${src}${SEP}test14198.d > ${output_file} 2>&1
grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} || exit 1

$DMD -m${MODEL} -I${src} -of${dir}${SEP}test14198b${EXE} -version=bug14198 ${src}${SEP}test14198.d > ${output_file} 2>&1
grep -q "_D8std141984conv11__T2toTAyaZ9__T2toTbZ2toFNaNbNiNfbZAya" ${output_file} || exit 1

rm ${dir}/{test14198b${OBJ},test14198b${EXE}}

echo Success > ${output_file}

0 comments on commit f22d9db

Please sign in to comment.