Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicit Conversion of Template Instantiations #16314

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 93 additions & 10 deletions compiler/src/dmd/mtype.d
Original file line number Diff line number Diff line change
Expand Up @@ -3995,42 +3995,125 @@
//printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars());

auto tos = to.isTypeStruct();
if (!(tos && sym == tos.sym))
if (!tos)
return MATCH.nomatch;

if (mod == to.mod)
return MATCH.exact;
if (sym == tos.sym)
{
/* They are the same struct
*/
if (mod == to.mod)
return MATCH.exact;

if (MODimplicitConv(mod, to.mod))
return MATCH.constant;
if (MODimplicitConv(mod, to.mod))
return MATCH.constant;

/* Check all the fields. If they can all be converted,
* allow the conversion.
*/
MATCH m = MATCH.constant;
uint offset = ~0; // must never match a field offset
foreach (v; sym.fields[])
{
/* Why are we only looking at the first member of a union?
* The check should check for overlap of v with the previous field,
* not just starting at the same point
*/
if (v.offset == offset) // v is at same offset as previous field
continue; // ignore

Type tvf = v.type.addMod(mod); // from type
Type tvt = v.type.addMod(to.mod); // to type

// field match
MATCH mf = tvf.implicitConvTo(tvt);
//printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf);

if (mf == MATCH.nomatch)
return MATCH.nomatch;
if (mf < m) // if field match is worse
m = mf;
offset = v.offset;
}
return m;
}

/* If `this` and `to` are different instances of the same template,
* we can check for structural conformance. Otherwise, nomatch.
* This implements Implicit Conversion of Template Instantiations.
*/
enum log = false;
if (log) printf("TypeStruct::implicitConvToWithoutAliasThis(%s => %s)\n", toChars(), to.toChars());
auto parent = sym.toParent();
auto parentto = tos.sym.toParent();
if (!parent || !parentto)
return MATCH.nomatch;

Check warning on line 4050 in compiler/src/dmd/mtype.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/mtype.d#L4050

Added line #L4050 was not covered by tests
auto inst = parent.isTemplateInstance();
auto instto = parentto.isTemplateInstance();
if (!inst || !instto)
return MATCH.nomatch;
if (inst.tempdecl != instto.tempdecl)
return MATCH.nomatch;

if (sym.alignment != tos.sym.alignment ||
sym.alignsize != tos.sym.alignsize ||
sym.structsize != tos.sym.structsize ||
sym.ispod != tos.sym.ispod)
return MATCH.nomatch;

if (log) printf("from kind: %s sym: %s\n", sym.toParent().kind(), sym.toParent().toChars());
if (log) printf("to kind: %s sym: %s\n", tos.sym.toParent().kind(), tos.sym.toParent().toChars());

MATCH m = mod == to.mod ? MATCH.exact : MATCH.constant;

/* Check all the fields. If they can all be converted,
* allow the conversion.
*/
MATCH m = MATCH.constant;
uint offset = ~0; // must never match a field offset
foreach (v; sym.fields[])
if (sym.fields.length != tos.sym.fields.length)
return MATCH.nomatch;

foreach (i, v; sym.fields[])
{
auto tov = tos.sym.fields[i];
if (tov.offset != v.offset)
return MATCH.nomatch;

Check warning on line 4080 in compiler/src/dmd/mtype.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/mtype.d#L4080

Added line #L4080 was not covered by tests

auto tfr = v.type;
auto tto = tov.type;
if (tfr.size() != tto.size())
return MATCH.nomatch;

/* Why are we only looking at the first member of a union?
* The check should check for overlap of v with the previous field,
* not just starting at the same point
*/
if (v.offset == offset) // v is at same offset as previous field
continue; // ignore

Type tvf = v.type.addMod(mod); // from type
Type tvt = v.type.addMod(to.mod); // to type
MATCH mf = tfr.implicitConvTo(tto); // try implicitly converting field
if (mf < MATCH.constant)
return MATCH.nomatch;
if (mf < m)
m = mf; // remember worst match

Check warning on line 4098 in compiler/src/dmd/mtype.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/mtype.d#L4098

Added line #L4098 was not covered by tests

Type tvf = tfr.addMod(mod); // from type
Type tvt = tfr.addMod(to.mod); // to type

// field match
MATCH mf = tvf.implicitConvTo(tvt);
mf = tvf.implicitConvTo(tvt);
//printf("\t%s => %s, match = %d\n", v.type.toChars(), tvt.toChars(), mf);
if (mf < MATCH.constant)
return MATCH.nomatch;

Check warning on line 4107 in compiler/src/dmd/mtype.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/mtype.d#L4107

Added line #L4107 was not covered by tests

if (mf == MATCH.nomatch)
return MATCH.nomatch;
if (mf < m) // if field match is worse
m = mf;

offset = v.offset;
}
if (log) printf("MATCH: %d\n", m);
return m;
}

Expand Down
77 changes: 77 additions & 0 deletions compiler/test/fail_compilation/imptmpcnv.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* Implicit Conversion of Template Instantiations
*/
/* TEST_OUTPUT:
---
fail_compilation/imptmpcnv.d(31): Error: cannot implicitly convert expression `a` of type `S2!(const(int))` to `const(S2!int)`
fail_compilation/imptmpcnv.d(48): Error: cannot implicitly convert expression `a` of type `S3!(const(int))` to `const(S3!int)`
fail_compilation/imptmpcnv.d(62): Error: cannot implicitly convert expression `a` of type `S4!(const(int))` to `const(S4!int)`
fail_compilation/imptmpcnv.d(76): Error: cannot implicitly convert expression `a` of type `S5!(const(int))` to `const(S5!int)`
---
*/


/*************************/

struct S1(T) { T t; }

void foo1()
{
S1!(const int) a;
const(S1!int) b = a;
S1!(const int) c = b;
}

/*************************/

struct S2(T) { T* t; }

void foo2()
{
S2!(const int) a;
const(S2!int) b = a;
}

/*************************/

struct S3(T)
{
static if (is(T == const))
{
int x;
}
T t;
}

void foo3()
{
S3!(const int) a;
const(S3!int) b = a;
}

/*************************/

struct S4(T)
{
static if (is(T == const)) { int x; } else { long x; }
T t;
}

void foo4()
{
S4!(const int) a;
const(S4!int) b = a;
}

/*************************/

struct S5(T)
{
T t;
static if (is(T == const)) { int x; } else { long x; }
}

void foo5()
{
S5!(const int) a;
const(S5!int) b = a;
}
Loading