Skip to content

Commit

Permalink
Move some semantic functions from func.d to funcsem.d and typesem.d (#…
Browse files Browse the repository at this point in the history
…16531)

Co-authored-by: Alex Muscar <[email protected]>
  • Loading branch information
muscar and Alex Muscar authored May 27, 2024
1 parent b0d277c commit d1f5a7e
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 162 deletions.
1 change: 0 additions & 1 deletion compiler/src/dmd/frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -3814,7 +3814,6 @@ class FuncDeclaration : public Declaration
bool needsClosure();
bool checkClosure();
bool hasNestedFrameRefs();
static bool needsFensure(FuncDeclaration* fd);
ParameterList getParameterList();
static FuncDeclaration* genCfunc(Array<Parameter* >* fparams, Type* treturn, const char* name, StorageClass stc = 0);
static FuncDeclaration* genCfunc(Array<Parameter* >* fparams, Type* treturn, Identifier* id, StorageClass stc = 0);
Expand Down
159 changes: 0 additions & 159 deletions compiler/src/dmd/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -1501,27 +1501,6 @@ extern (C++) class FuncDeclaration : Declaration
return false;
}

/****************************************************
* Determine whether an 'out' contract is declared inside
* the given function or any of its overrides.
* Params:
* fd = the function to search
* Returns:
* true found an 'out' contract
*/
static bool needsFensure(FuncDeclaration fd) @safe
{
if (fd.fensures)
return true;

foreach (fdv; fd.foverrides)
{
if (needsFensure(fdv))
return true;
}
return false;
}

/*********************************************
* Returns: the function's parameter list, and whether
* it is variadic or not.
Expand Down Expand Up @@ -1578,127 +1557,6 @@ extern (C++) class FuncDeclaration : Declaration
return fd;
}

/+
+ Checks the parameter and return types iff this is a `main` function.
+
+ The following signatures are allowed for a `D main`:
+ - Either no or a single parameter of type `string[]`
+ - Return type is either `void`, `int` or `noreturn`
+
+ The following signatures are standard C:
+ - `int main()`
+ - `int main(int, char**)`
+
+ This function accepts the following non-standard extensions:
+ - `char** envp` as a third parameter
+ - `void` / `noreturn` as return type
+
+ This function will issue errors for unexpected arguments / return types.
+/
extern (D) final void checkMain()
{
if (ident != Id.main || isMember() || isNested())
return; // Not a main function

TypeFunction tf = type.toTypeFunction();

Type retType = tf.nextOf();
if (!retType)
{
// auto main(), check after semantic
assert(this.inferRetType);
return;
}

/// Checks whether `t` is equivalent to `char**`
/// Ignores qualifiers and treats enums according to their base type
static bool isCharPtrPtr(Type t)
{
auto tp = t.toBasetype().isTypePointer();
if (!tp)
return false;

tp = tp.next.toBasetype().isTypePointer();
if (!tp)
return false;

return tp.next.toBasetype().ty == Tchar;
}

// Neither of these qualifiers is allowed because they affect the ABI
enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_;

const nparams = tf.parameterList.length;
bool argerr;

const linkage = resolvedLinkage();
if (linkage == LINK.d)
{
if (nparams == 1)
{
auto fparam0 = tf.parameterList[0];
auto t = fparam0.type.toBasetype();
if (t.ty != Tarray ||
t.nextOf().ty != Tarray ||
t.nextOf().nextOf().ty != Tchar ||
fparam0.storageClass & invalidSTC)
{
argerr = true;
}
}

if (tf.parameterList.varargs || nparams >= 2 || argerr)
.error(loc, "%s `%s` parameter list must be empty or accept one parameter of type `string[]`", kind, toPrettyChars);
}

else if (linkage == LINK.c)
{
if (nparams == 2 || nparams == 3)
{
// Argument count must be int
auto argCount = tf.parameterList[0];
argerr |= !!(argCount.storageClass & invalidSTC);
argerr |= argCount.type.toBasetype().ty != Tint32;

// Argument pointer must be char**
auto argPtr = tf.parameterList[1];
argerr |= !!(argPtr.storageClass & invalidSTC);
argerr |= !isCharPtrPtr(argPtr.type);

// `char** environ` is a common extension, see J.5.1 of the C standard
if (nparams == 3)
{
auto envPtr = tf.parameterList[2];
argerr |= !!(envPtr.storageClass & invalidSTC);
argerr |= !isCharPtrPtr(envPtr.type);
}
}
else
argerr = nparams != 0;

// Disallow variadic main() - except for K&R declarations in C files.
// E.g. int main(), int main(argc, argv) int argc, char** argc { ... }
if (tf.parameterList.varargs && (!this.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams)))
argerr |= true;

if (argerr)
{
.error(loc, "%s `%s` parameters must match one of the following signatures", kind, toPrettyChars);
loc.errorSupplemental("`main()`");
loc.errorSupplemental("`main(int argc, char** argv)`");
loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]");
}
}
else
return; // Neither C nor D main, ignore (should probably be an error)

// Allow enums with appropriate base types (same ABI)
retType = retType.toBasetype();

if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn)
.error(loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", kind, toPrettyChars, tf.nextOf().toChars());
}

/***********************************************
* Check all return statements for a function to verify that returning
* using NRVO is possible.
Expand Down Expand Up @@ -1960,23 +1818,6 @@ unittest
assert(mismatches.isMutable);
}

/**************************************
* Returns an indirect type one step from t.
*/
Type getIndirection(Type t)
{
t = t.baseElemOf();
if (t.ty == Tarray || t.ty == Tpointer)
return t.nextOf().toBasetype();
if (t.ty == Taarray || t.ty == Tclass)
return t;
if (t.ty == Tstruct)
return t.hasPointers() ? t : null; // TODO

// should consider TypeDelegate?
return null;
}

/**************************************
* Performs type-based alias analysis between a newly created value and a pre-
* existing memory reference:
Expand Down
144 changes: 143 additions & 1 deletion compiler/src/dmd/funcsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2602,6 +2602,27 @@ void buildEnsureRequire(FuncDeclaration thisfd)
}
}

/****************************************************
* Determine whether an 'out' contract is declared inside
* the given function or any of its overrides.
* Params:
* fd = the function to search
* Returns:
* true found an 'out' contract
*/
bool needsFensure(FuncDeclaration fd) @safe
{
if (fd.fensures)
return true;

foreach (fdv; fd.foverrides)
{
if (needsFensure(fdv))
return true;
}
return false;
}

/****************************************************
* Merge into this function the 'out' contracts of all it overrides.
* 'out's are AND'd together, i.e. all of them need to pass.
Expand All @@ -2624,7 +2645,7 @@ Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Express
* https://issues.dlang.org/show_bug.cgi?id=3602 and
* https://issues.dlang.org/show_bug.cgi?id=5230
*/
if (fd.needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done)
if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done)
{
assert(fdv._scope);
Scope* sc = fdv._scope.push();
Expand Down Expand Up @@ -2844,3 +2865,124 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char)
return false;
}
}

/+
+ Checks the parameter and return types iff this is a `main` function.
+
+ The following signatures are allowed for a `D main`:
+ - Either no or a single parameter of type `string[]`
+ - Return type is either `void`, `int` or `noreturn`
+
+ The following signatures are standard C:
+ - `int main()`
+ - `int main(int, char**)`
+
+ This function accepts the following non-standard extensions:
+ - `char** envp` as a third parameter
+ - `void` / `noreturn` as return type
+
+ This function will issue errors for unexpected arguments / return types.
+/
extern (D) void checkMain(FuncDeclaration fd)
{
if (fd.ident != Id.main || fd.isMember() || fd.isNested())
return; // Not a main function

TypeFunction tf = fd.type.toTypeFunction();

Type retType = tf.nextOf();
if (!retType)
{
// auto main(), check after semantic
assert(fd.inferRetType);
return;
}

/// Checks whether `t` is equivalent to `char**`
/// Ignores qualifiers and treats enums according to their base type
static bool isCharPtrPtr(Type t)
{
auto tp = t.toBasetype().isTypePointer();
if (!tp)
return false;

tp = tp.next.toBasetype().isTypePointer();
if (!tp)
return false;

return tp.next.toBasetype().ty == Tchar;
}

// Neither of these qualifiers is allowed because they affect the ABI
enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_;

const nparams = tf.parameterList.length;
bool argerr;

const linkage = fd.resolvedLinkage();
if (linkage == LINK.d)
{
if (nparams == 1)
{
auto fparam0 = tf.parameterList[0];
auto t = fparam0.type.toBasetype();
if (t.ty != Tarray ||
t.nextOf().ty != Tarray ||
t.nextOf().nextOf().ty != Tchar ||
fparam0.storageClass & invalidSTC)
{
argerr = true;
}
}

if (tf.parameterList.varargs || nparams >= 2 || argerr)
.error(fd.loc, "%s `%s` parameter list must be empty or accept one parameter of type `string[]`", fd.kind, fd.toPrettyChars);
}

else if (linkage == LINK.c)
{
if (nparams == 2 || nparams == 3)
{
// Argument count must be int
auto argCount = tf.parameterList[0];
argerr |= !!(argCount.storageClass & invalidSTC);
argerr |= argCount.type.toBasetype().ty != Tint32;

// Argument pointer must be char**
auto argPtr = tf.parameterList[1];
argerr |= !!(argPtr.storageClass & invalidSTC);
argerr |= !isCharPtrPtr(argPtr.type);

// `char** environ` is a common extension, see J.5.1 of the C standard
if (nparams == 3)
{
auto envPtr = tf.parameterList[2];
argerr |= !!(envPtr.storageClass & invalidSTC);
argerr |= !isCharPtrPtr(envPtr.type);
}
}
else
argerr = nparams != 0;

// Disallow variadic main() - except for K&R declarations in C files.
// E.g. int main(), int main(argc, argv) int argc, char** argc { ... }
if (tf.parameterList.varargs && (!fd.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams)))
argerr |= true;

if (argerr)
{
.error(fd.loc, "%s `%s` parameters must match one of the following signatures", fd.kind, fd.toPrettyChars);
fd.loc.errorSupplemental("`main()`");
fd.loc.errorSupplemental("`main(int argc, char** argv)`");
fd.loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]");
}
}
else
return; // Neither C nor D main, ignore (should probably be an error)

// Allow enums with appropriate base types (same ABI)
retType = retType.toBasetype();

if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn)
.error(fd.loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", fd.kind, fd.toPrettyChars, tf.nextOf().toChars());
}
2 changes: 1 addition & 1 deletion compiler/src/dmd/semantic3.d
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
fds.checkInContractOverrides();

// Remember whether we need to generate an 'out' contract.
immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl);
immutable bool needEnsure = funcdecl.needsFensure();

if (funcdecl.fbody || funcdecl.frequires || needEnsure)
{
Expand Down
17 changes: 17 additions & 0 deletions compiler/src/dmd/typesem.d
Original file line number Diff line number Diff line change
Expand Up @@ -1282,6 +1282,23 @@ bool hasPointers(Type t)
}
}

/**************************************
* Returns an indirect type one step from t.
*/
Type getIndirection(Type t)
{
t = t.baseElemOf();
if (t.ty == Tarray || t.ty == Tpointer)
return t.nextOf().toBasetype();
if (t.ty == Taarray || t.ty == Tclass)
return t;
if (t.ty == Tstruct)
return t.hasPointers() ? t : null; // TODO

// should consider TypeDelegate?
return null;
}

/******************************************
* Perform semantic analysis on a type.
* Params:
Expand Down

0 comments on commit d1f5a7e

Please sign in to comment.