Skip to content

Commit

Permalink
Fix 18472: betterC cannot use format at compile time.
Browse files Browse the repository at this point in the history
  • Loading branch information
Etienne Cimon committed Feb 23, 2023
1 parent 0629537 commit 6b6a370
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 8 deletions.
8 changes: 8 additions & 0 deletions compiler/src/dmd/dinterpret.d
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,12 @@ public Expression ctfeInterpret(Expression e)

auto rgnpos = ctfeGlobals.region.savePos();

ctfeGlobals.active = true;

Expression result = interpret(e, null);

ctfeGlobals.active = false;

// Report an error if the expression contained a `ThrowException` and
// hence generated an uncaught exception
if (auto tee = result.isThrownExceptionExp())
Expand Down Expand Up @@ -206,6 +210,9 @@ void incArrayAllocs()
++ctfeGlobals.numArrayAllocs;
}

bool isInterpreting() {
return ctfeGlobals.active;
}
/* ================================================ Implementation ======================================= */

private:
Expand All @@ -227,6 +234,7 @@ struct CtfeGlobals
int maxCallDepth = 0; // highest number of recursive calls
int numArrayAllocs = 0; // Number of allocated arrays
int numAssignments = 0; // total number of assignments executed
bool active = false; // whether we are interpreting a ctfe function
}

__gshared CtfeGlobals ctfeGlobals;
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dmd/dscope.d
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ enum SCOPE
compile = 0x0100, /// inside __traits(compile)
ignoresymbolvisibility = 0x0200, /// ignore symbol visibility
/// https://issues.dlang.org/show_bug.cgi?id=15907
ctfeonly = 0x0400,
Cfile = 0x0800, /// C semantics apply
free = 0x8000, /// is on free list

Expand All @@ -68,7 +69,7 @@ enum SCOPE
/// Flags that are carried along with a scope push()
private enum PersistentFlags =
SCOPE.contract | SCOPE.debug_ | SCOPE.ctfe | SCOPE.compile | SCOPE.constraint |
SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility |
SCOPE.noaccesscheck | SCOPE.ignoresymbolvisibility | SCOPE.ctfeonly |
SCOPE.Cfile;

extern (C++) struct Scope
Expand All @@ -90,6 +91,7 @@ extern (C++) struct Scope
ForeachStatement fes; /// if nested function for ForeachStatement, this is it
Scope* callsc; /// used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__
Dsymbol inunion; /// != null if processing members of a union
bool skipretnogc;
bool nofree; /// true if shouldn't free it
bool inLoop; /// true if inside a loop (where constructor calls aren't allowed)
int intypeof; /// in typeof(exp)
Expand Down
8 changes: 7 additions & 1 deletion compiler/src/dmd/e2ir.d
Original file line number Diff line number Diff line change
Expand Up @@ -6084,7 +6084,13 @@ private
elem *getTypeInfo(Expression e, Type t, IRState* irs)
{
assert(t.ty != Terror);
genTypeInfo(e, e.loc, t, null);
Scope* sc;
if (irs && irs.ctfeOnly > 0)
{
sc = new Scope;
sc.flags |= SCOPE.ctfeonly;
}
genTypeInfo(e, e.loc, t, sc);
elem* result = el_ptr(toSymbol(t.vtinfo));
return result;
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/nogc.d
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public:
Expression checkGC(Scope* sc, Expression e)
{
FuncDeclaration f = sc.func;
if (e && e.op != EXP.error && f && sc.intypeof != 1 && !(sc.flags & SCOPE.ctfe) &&
if (e && e.op != EXP.error && f && sc.intypeof != 1 && !(sc.flags & SCOPE.ctfe) && !(sc.flags & SCOPE.ctfeonly) &&
(f.type.ty == Tfunction &&
(cast(TypeFunction)f.type).isnogc || f.nogcInprocess || global.params.vgc) &&
!(sc.flags & SCOPE.debug_))
Expand Down
16 changes: 16 additions & 0 deletions compiler/src/dmd/s2ir.d
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,18 @@ private extern (C++) class S2irVisitor : Visitor
//printf("ExpStatement.toIR(), exp: %p %s\n", s.exp, s.exp ? s.exp.toChars() : "");
if (s.exp)
{
if (stmtstate.prev)
{
if (auto ae = s.exp.isAssertExp())
{
if (!stmtstate.prev.ctfeOnly && ae.e1 && ae.e1.isVarExp() && ae.e1.isVarExp().var
&& ae.e1.isVarExp().var.ident == Id.ctfe)
{
irs.ctfeOnly++;
stmtstate.prev.ctfeOnly = true;
}
}
}
if (s.exp.hasCode &&
!(isAssertFalse(s.exp))) // `assert(0)` not meant to be covered
incUsage(irs, s.loc);
Expand Down Expand Up @@ -1536,6 +1548,8 @@ private extern (C++) class S2irVisitor : Visitor
{
scope v = new S2irVisitor(irs, stmtstate);
s.accept(v);
if (irs && stmtstate && irs.ctfeOnly > 0 && stmtstate.ctfeOnly)
irs.ctfeOnly--;
}
}

Expand All @@ -1556,6 +1570,8 @@ void Statement_toIR(Statement s, IRState *irs)
StmtState stmtstate;
scope v = new S2irVisitor(irs, &stmtstate);
s.accept(v);
if (irs && irs.ctfeOnly > 0 && stmtstate.ctfeOnly)
irs.ctfeOnly--;
}

/***************************************************
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dmd/semantic3.d
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
if (funcdecl.ident != Id.require && funcdecl.ident != Id.ensure)
sc2.flags = sc.flags & ~SCOPE.contract;
sc2.flags &= ~SCOPE.compile;
sc2.flags &= ~SCOPE.ctfeonly;
sc2.tf = null;
sc2.os = null;
sc2.inLoop = false;
Expand Down Expand Up @@ -930,7 +931,8 @@ private extern(C++) final class Semantic3Visitor : Visitor
checkReturnEscape(sc2, exp, false);
}

exp = checkGC(sc2, exp);
if (!sc2.func.skipretnogc)
exp = checkGC(sc2, exp);

if (funcdecl.vresult)
{
Expand Down
15 changes: 15 additions & 0 deletions compiler/src/dmd/statementsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,21 @@ package (dmd) extern (C++) final class StatementSemanticVisitor : Visitor

s.exp = s.exp.expressionSemantic(sc);
s.exp = resolveProperties(sc, s.exp);

if (sc)
{
if (auto ae = s.exp.isAssertExp())
{
if (ae.e1 && ae.e1.isVarExp() && ae.e1.isVarExp().var
&& ae.e1.isVarExp().var.ident == Id.ctfe)
{
//printf("ExpStatement::semantic() ctfeonly %s\n", s.exp.toChars());
sc.flags |= SCOPE.ctfeonly;
sc.func.skipretnogc = true;
}
}
}

s.exp = s.exp.addDtorHook(sc);
if (checkNonAssignmentArrayOp(s.exp))
s.exp = ErrorExp.get();
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/stmtstate.d
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ struct StmtState(block)
block* finallyBlock;
block* tryBlock;

bool ctfeOnly;

this(StmtState* prev, Statement statement)
{
this.prev = prev;
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dmd/toir.d
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ struct IRState
const Target* target; // target
bool mayThrow; // the expression being evaluated may throw
bool Cfile; // use C semantics
int ctfeOnly; // the expression will not generate TypeInfo if >0

this(Module m, FuncDeclaration fd, Array!(elem*)* varsInScope, Dsymbols* deferToObj, Label*[void*]* labels,
const Param* params, const Target* target)
Expand All @@ -102,6 +103,7 @@ struct IRState
&& ClassDeclaration.throwable
&& !(fd && fd.hasNoEH);
this.Cfile = m.filetype == FileType.c;
this.ctfeOnly = 0;
}

FuncDeclaration getFunc()
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dmd/toobj.d
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,8 @@ void toObjFile(Dsymbol ds, bool multiobj)

override void visit(TypeInfoDeclaration tid)
{
if (isSpeculativeType(tid.tinfo))
import dmd.globals : global;
if (isSpeculativeType(tid.tinfo) || global.params.betterC)
{
//printf("-speculative '%s'\n", tid.toPrettyChars());
return;
Expand Down
7 changes: 4 additions & 3 deletions compiler/src/dmd/typinf.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import dmd.declaration;
import dmd.dmodule;
import dmd.dscope;
import dmd.dclass;
import dmd.dinterpret;
import dmd.dstruct;
import dmd.errors;
import dmd.expression;
Expand All @@ -41,7 +42,7 @@ extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope
// Even when compiling without `useTypeInfo` (e.g. -betterC) we should
// still be able to evaluate `TypeInfo` at compile-time, just not at runtime.
// https://issues.dlang.org/show_bug.cgi?id=18472
if (!sc || !(sc.flags & SCOPE.ctfe))
if (!sc || (!(sc.flags & (SCOPE.ctfe | SCOPE.ctfeonly)) && !isInterpreting()))
{
if (!global.params.useTypeInfo)
{
Expand Down Expand Up @@ -81,13 +82,13 @@ extern (C++) void genTypeInfo(Expression e, const ref Loc loc, Type torig, Scope
// druntime
if (!isUnqualifiedClassInfo && !builtinTypeInfo(t))
{
if (sc) // if in semantic() pass
if (sc && sc._module) // if in semantic() pass
{
// Find module that will go all the way to an object file
Module m = sc._module.importedFrom;
m.members.push(t.vtinfo);
}
else // if in obj generation pass
else if (!sc) // if in obj generation pass
{
toObjFile(t.vtinfo, global.params.multiobj);
}
Expand Down
52 changes: 52 additions & 0 deletions compiler/test/compilable/b18472.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* REQUIRED_ARGS: -betterC
*/

/*******************************************/
// https://issues.dlang.org/show_bug.cgi?id=18472
@nogc nothrow pure:
immutable(Char)[] format(Char, Args...)(in Char[] fmt, Args args)
{

if (__ctfe)
{
assert(__ctfe);
auto data2 = new char[5];
auto data = new Data2;
{
auto data3 = new Data2;
}
data2 = cast(char[]) "test2";
return data2;
}
else
{
return "test";
}
}

extern(C) void main()
{
static assert(getData() == "test");
static assert("%s %s".format("test", "test") == "test2", "Not working");
assert("%s %s".format("test", "test") == "test", "%s %s".format("test", "test"));
assert(getData() == "test2", getData());
}

string getData() {
if (__ctfe)
{
assert(__ctfe);
auto data2 = new ubyte[5];
auto data = new Data2;
return "test";
}
else
{
return "test2";
}
}

private struct Data2
{
size_t capacity;
}
24 changes: 24 additions & 0 deletions compiler/test/fail_compilation/b18472.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// REQUIRED_ARGS: -betterC
/*
TEST_OUTPUT:
---
fail_compilation/b18472.d(14): Error: cannot use `new` in `@nogc` function `b18472.getFun.gcAlloc2`
---
*/
@nogc:

auto getFun() {
if (__ctfe)
{
assert(__ctfe);
static ubyte[] gcAlloc2() @nogc {return new ubyte[10];}
return &gcAlloc2;
}
return null;
}

immutable fun = getFun();

extern(C) void main() {
fun();
}

0 comments on commit 6b6a370

Please sign in to comment.