Merge DMD 1.057.

This commit is contained in:
Christian Kamm
2010-03-08 21:39:20 +01:00
parent ef066d42c0
commit 1d488da835
17 changed files with 347 additions and 80 deletions

View File

@@ -1,6 +1,6 @@
// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
@@ -13,6 +13,7 @@
#include <assert.h>
#include "rmem.h"
#include "speller.h"
#include "mars.h"
#include "dsymbol.h"
@@ -347,6 +348,27 @@ Dsymbol *Dsymbol::search(Loc loc, Identifier *ident, int flags)
return NULL;
}
/***************************************************
* Search for symbol with correct spelling.
*/
void *symbol_search_fp(void *arg, const char *seed)
{
Dsymbol *s = (Dsymbol *)arg;
Identifier id(seed, 0);
Module::clearCache();
s = s->search(0, &id, 4|2);
return s;
}
Dsymbol *Dsymbol::search_correct(Identifier *ident)
{
if (global.gag)
return NULL; // don't do it for speculative compiles; too time consuming
return (Dsymbol *)speller(ident->toChars(), &symbol_search_fp, this, idchars);
}
/***************************************
* Search for identifier id as a member of 'this'.
* id may be a template instance.

View File

@@ -1,6 +1,6 @@
// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
@@ -167,6 +167,7 @@ struct Dsymbol : Object
virtual void semantic3(Scope *sc);
virtual void inlineScan();
virtual Dsymbol *search(Loc loc, Identifier *ident, int flags);
Dsymbol *search_correct(Identifier *id);
Dsymbol *searchX(Loc loc, Scope *sc, Identifier *id);
virtual int overloadInsert(Dsymbol *s);
#ifdef _DH

View File

@@ -55,6 +55,7 @@ int isnan(double);
#include "hdrgen.h"
#include "parse.h"
Expression *expandVar(int result, VarDeclaration *v);
#define LOGSEMANTIC 0
@@ -1059,13 +1060,10 @@ void Expression::error(const char *format, ...)
void Expression::warning(const char *format, ...)
{
if (global.params.warnings && !global.gag)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
void Expression::rvalue()
@@ -1076,7 +1074,7 @@ void Expression::rvalue()
dump(0);
halt();
#endif
type = Type::tint32;
type = Type::terror;
}
}
@@ -1138,6 +1136,9 @@ void Expression::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
void Expression::toMangleBuffer(OutBuffer *buf)
{
error("expression %s is not a valid template value argument", toChars());
#ifdef DEBUG
dump(0);
#endif
}
/***************************************
@@ -1666,7 +1667,18 @@ void IntegerExp::toMangleBuffer(OutBuffer *buf)
if ((sinteger_t)value < 0)
buf->printf("N%jd", -value);
else
{
/* This is an awful hack to maintain backwards compatibility.
* There really always should be an 'i' before a number, but
* there wasn't in earlier implementations, so to maintain
* backwards compatibility it is only done if necessary to disambiguate.
* See bugzilla 3029
*/
if (buf->offset > 0 && isdigit(buf->data[buf->offset - 1]))
buf->writeByte('i');
buf->printf("%jd", value);
}
}
/******************************** ErrorExp **************************/
@@ -2103,7 +2115,21 @@ Expression *IdentifierExp::semantic(Scope *sc)
}
return e->semantic(sc);
}
error("undefined identifier %s", ident->toChars());
#if DMDV2
if (ident == Id::ctfe)
{ // Create the magic __ctfe bool variable
VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL);
Expression *e = new VarExp(loc, vd);
e = e->semantic(sc);
return e;
}
#endif
s = sc->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());
type = Type::terror;
return this;
}
@@ -5275,14 +5301,14 @@ Expression *FileExp::semantic(Scope *sc)
goto Lerror;
}
if (name != FileName::name(name))
{ error("use -Jpath switch to provide path for filename %s", name);
goto Lerror;
}
/* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
* ('Path Traversal') attacks.
* http://cwe.mitre.org/data/definitions/22.html
*/
name = FileName::searchPath(global.filePath, name, 0);
name = FileName::safeSearchPath(global.filePath, name);
if (!name)
{ error("file %s cannot be found, check -Jpath", se->toChars());
{ error("file %s cannot be found or not in a path specified with -J", se->toChars());
goto Lerror;
}
@@ -5820,6 +5846,14 @@ Expression *DotVarExp::semantic(Scope *sc)
e = e->semantic(sc);
return e;
}
if (v->init)
{ Expression *e = v->init->toExpression();
if (e)
{ e = e->copy();
e = e->semantic(sc);
return e;
}
}
}
}
}
@@ -5941,11 +5975,15 @@ L1:
e = e->semantic(sc);
if (e->op == TOKdottd)
{
if (global.errors)
return new ErrorExp(); // TemplateInstance::semantic() will fail anyway
DotTemplateExp *dte = (DotTemplateExp *)e;
TemplateDeclaration *td = dte->td;
eleft = dte->e1;
ti->tempdecl = td;
ti->semantic(sc);
if (!ti->inst) // if template failed to expand
return new ErrorExp();
Dsymbol *s = ti->inst->toAlias();
Declaration *v = s->isDeclaration();
if (v)
@@ -7377,6 +7415,12 @@ Expression *CastExp::semantic(Scope *sc)
}
}
}
if (!e1->type)
{ error("cannot cast %s", e1->toChars());
return new ErrorExp();
}
e = e1->castTo(sc, to);
return e;
}

View File

@@ -640,15 +640,15 @@ void FuncDeclaration::semantic(Scope *sc)
fdrequire = fd;
}
if (!outId && f->nextOf()->toBasetype()->ty != Tvoid)
outId = Id::result; // provide a default
if (fensure)
{ /* out (result) { ... }
* becomes:
* tret __ensure(ref tret result) { ... }
* __ensure(result);
*/
if (!outId && f->nextOf()->toBasetype()->ty != Tvoid)
outId = Id::result; // provide a default
Loc loc = fensure->loc;
Parameters *arguments = new Parameters();
Parameter *a = NULL;
@@ -772,14 +772,14 @@ void FuncDeclaration::semantic3(Scope *sc)
if (ad)
{ VarDeclaration *v;
if (isFuncLiteralDeclaration() && isNested())
if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof)
{
error("literals cannot be class members");
error("function literals cannot be class members");
return;
}
else
{
assert(!isNested()); // can't be both member and nested
assert(!isNested() || sc->intypeof); // can't be both member and nested
assert(ad->handle);
v = new ThisDeclaration(loc, ad->handle);
v->storage_class |= STCparameter | STCin;
@@ -2628,15 +2628,9 @@ const char *FuncLiteralDeclaration::kind()
void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
static Identifier *idfunc;
static Identifier *iddel;
if (!idfunc)
idfunc = new Identifier("function", 0);
if (!iddel)
iddel = new Identifier("delegate", 0);
type->toCBuffer(buf, ((tok == TOKdelegate) ? iddel : idfunc), hgs);
buf->writestring(kind());
buf->writeByte(' ');
type->toCBuffer(buf, NULL, hgs);
bodyToCBuffer(buf, hgs);
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
@@ -292,6 +292,11 @@ int BinExp::inlineCost(InlineCostState *ics)
int CallExp::inlineCost(InlineCostState *ics)
{
// Bugzilla 3500: super.func() calls must be devirtualized, and the inliner
// can't handle that at present.
if (e1->op == TOKdotvar && ((DotVarExp *)e1)->e1->op == TOKsuper)
return COST_MAX;
return 1 + e1->inlineCost(ics) + arrayInlineCost(ics, arguments);
}

View File

@@ -115,7 +115,8 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument
assert(tb->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)tb;
Type *tret = tf->next->toBasetype();
if (tf->varargs && arguments && parameters && arguments->dim != parameters->dim)
if (tf->varargs && arguments &&
((parameters && arguments->dim != parameters->dim) || (!parameters && arguments->dim)))
{ cantInterpret = 1;
error("C-style variadic functions are not yet implemented in CTFE");
return NULL;

View File

@@ -61,9 +61,9 @@ Global::Global()
obj_ext_alt = "obj";
#endif
copyright = "Copyright (c) 1999-2009 by Digital Mars and Tomas Lindquist Olsen";
copyright = "Copyright (c) 1999-2010 by Digital Mars and Tomas Lindquist Olsen";
written = "written by Walter Bright and Tomas Lindquist Olsen";
version = "v1.056";
version = "v1.057";
ldc_version = LDC_REV;
llvm_version = LLVM_REV_STR;
global.structalign = 8;
@@ -116,13 +116,10 @@ void error(Loc loc, const char *format, ...)
void warning(Loc loc, const char *format, ...)
{
if (global.params.warnings && !global.gag)
{
va_list ap;
va_start(ap, format);
vwarning(loc, format, ap);
va_end( ap );
}
va_list ap;
va_start(ap, format);
vwarning(loc, format, ap);
va_end( ap );
}
void verror(Loc loc, const char *format, va_list ap)
@@ -147,16 +144,26 @@ void vwarning(Loc loc, const char *format, va_list ap)
{
if (global.params.warnings && !global.gag)
{
char *p = loc.toChars();
char *p = loc.toChars();
if (*p)
fprintf(stdmsg, "%s: ", p);
mem.free(p);
if (*p)
fprintf(stdmsg, "%s: ", p);
mem.free(p);
fprintf(stdmsg, "Warning: ");
vfprintf(stdmsg, format, ap);
fprintf(stdmsg, "\n");
fflush(stdmsg);
fprintf(stdmsg, "Warning: ");
#if _MSC_VER
// MS doesn't recognize %zu format
OutBuffer tmp;
tmp.vprintf(format, ap);
fprintf(stdmsg, "%s", tmp.toChars());
#else
vfprintf(stdmsg, format, ap);
#endif
fprintf(stdmsg, "\n");
fflush(stdmsg);
//halt();
if (global.params.warnings == 1)
global.warnings++; // warnings don't count if gagged
}
}
@@ -184,6 +191,7 @@ void halt()
#endif
}
/***********************************
* Parse and append contents of environment variable envvar
* to argc and argv[].

View File

@@ -94,6 +94,7 @@ the target object file format:
#define STRUCTTHISREF DMDV2 // if 'this' for struct is a reference, not a pointer
#define SNAN_DEFAULT_INIT DMDV2 // if floats are default initialized to signalling NaN
#define SARRAYVALUE DMDV2 // static arrays are value types
#define MODULEINFO_IS_STRUCT DMDV2 // if ModuleInfo is a struct rather than a class
// Set if C++ mangling is done by the front end
#define CPP_MANGLE (DMDV2 && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS))
@@ -182,6 +183,8 @@ struct Param
bool useInline; // inline expand functions
bool warnings; // enable warnings
ubyte Dversion; // D version number
// 1: warnings as errors
// 2: informational warnings (no errors)
char safe; // enforce safe memory model
char *argv0; // program name
@@ -283,7 +286,8 @@ struct Global
Param params;
unsigned errors; // number of errors reported so far
unsigned gag; // !=0 means gag reporting of errors
unsigned warnings; // number of warnings reported so far
unsigned gag; // !=0 means gag reporting of errors & warnings
Global();
};
@@ -426,10 +430,9 @@ typedef uint64_t StorageClass;
void warning(Loc loc, const char *format, ...) IS_PRINTF(2);
void vwarning(Loc loc, const char *format, va_list);
void error(Loc loc, const char *format, ...) IS_PRINTF(2);
void verror(Loc loc, const char *format, va_list);
void vwarning(Loc loc, const char *format, va_list);
#ifdef __GNUC__
__attribute__((noreturn))
#endif

View File

@@ -905,6 +905,7 @@ void Module::gensymfile()
int Module::needModuleInfo()
{
//printf("needModuleInfo() %s, %d, %d\n", toChars(), needmoduleinfo, global.params.cov);
return needmoduleinfo;
}
@@ -943,6 +944,13 @@ Dsymbol *Module::symtabInsert(Dsymbol *s)
return Package::symtabInsert(s);
}
void Module::clearCache()
{
for (int i = 0; i < amodules.dim; i++)
{ Module *m = (Module *)amodules.data[i];
m->searchCacheIdent = NULL;
}
}
/*******************************************
* Can't run semantic on s now, try again later.

View File

@@ -154,6 +154,7 @@ struct Module : Package
void deleteObjFile();
void addDeferredSemantic(Dsymbol *s);
static void runDeferredSemantic();
static void clearCache();
int imports(Module *m);
// Back end

View File

@@ -1,6 +1,6 @@
// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
@@ -135,6 +135,10 @@ Type::Type(TY ty, Type *next)
#if DMDV2
this->cto = NULL;
this->ito = NULL;
this->sto = NULL;
this->scto = NULL;
this->wto = NULL;
this->swto = NULL;
#endif
this->pto = NULL;
this->rto = NULL;
@@ -641,7 +645,7 @@ Expression *Type::getProperty(Loc loc, Identifier *ident)
else if (ident == Id::size)
{
error(loc, ".size property should be replaced with .sizeof");
e = new IntegerExp(loc, size(loc), Type::tsize_t);
e = new ErrorExp();
}
else if (ident == Id::alignof)
{
@@ -679,8 +683,16 @@ Expression *Type::getProperty(Loc loc, Identifier *ident)
}
else
{
error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
e = new IntegerExp(loc, 1, Type::tint32);
Dsymbol *s = NULL;
if (ty == Tstruct || ty == Tclass || ty == Tenum || ty == Ttypedef)
s = toDsymbol(NULL);
if (s)
s = s->search_correct(ident);
if (s)
error(loc, "no property '%s' for type '%s', did you mean '%s'?", ident->toChars(), toChars(), s->toChars());
else
error(loc, "no property '%s' for type '%s'", ident->toChars(), toChars());
e = new ErrorExp();
}
return e;
}
@@ -785,13 +797,10 @@ void Type::error(Loc loc, const char *format, ...)
void Type::warning(Loc loc, const char *format, ...)
{
if (global.params.warnings && !global.gag)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
Identifier *Type::getTypeInfoIdent(int internal)
@@ -4630,7 +4639,11 @@ L1:
TemplateInstance *ti = s->isTemplateInstance();
if (ti)
{ if (!ti->semanticRun)
{
if (global.errors)
return new ErrorExp(); // TemplateInstance::semantic() will fail anyway
ti->semantic(sc);
}
s = ti->inst->toAlias();
if (!s->isTemplateInstance())
goto L1;
@@ -5071,7 +5084,11 @@ L1:
TemplateInstance *ti = s->isTemplateInstance();
if (ti)
{ if (!ti->semanticRun)
{
if (global.errors)
return new ErrorExp(); // TemplateInstance::semantic() will fail anyway
ti->semantic(sc);
}
s = ti->inst->toAlias();
if (!s->isTemplateInstance())
goto L1;

View File

@@ -1,8 +1,8 @@
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// www.digitalmars.com
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
@@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
@@ -819,6 +820,91 @@ char *FileName::searchPath(Array *path, const char *name, int cwd)
return NULL;
}
/*************************************
* Search Path for file in a safe manner.
*
* Be wary of CWE-22: Improper Limitation of a Pathname to a Restricted Directory
* ('Path Traversal') attacks.
* http://cwe.mitre.org/data/definitions/22.html
* More info:
* https://www.securecoding.cert.org/confluence/display/seccode/FIO02-C.+Canonicalize+path+names+originating+from+untrusted+sources
* Returns:
* NULL file not found
* !=NULL mem.malloc'd file name
*/
char *FileName::safeSearchPath(Array *path, const char *name)
{
#if _WIN32
/* Disallow % / \ : and .. in name characters
*/
for (const char *p = name; *p; p++)
{
char c = *p;
if (c == '\\' || c == '/' || c == ':' || c == '%' ||
(c == '.' && p[1] == '.'))
{
return NULL;
}
}
return FileName::searchPath(path, name, 0);
#elif POSIX
/* Even with realpath(), we must check for // and disallow it
*/
for (const char *p = name; *p; p++)
{
char c = *p;
if (c == '/' && p[1] == '/')
{
return NULL;
}
}
if (path)
{ unsigned i;
/* Each path is converted to a cannonical name and then a check is done to see
* that the searched name is really a child one of the the paths searched.
*/
for (i = 0; i < path->dim; i++)
{
char *cname = NULL;
char *cpath = canonicalName((char *)path->data[i]);
//printf("FileName::safeSearchPath(): name=%s; path=%s; cpath=%s\n",
// name, (char *)path->data[i], cpath);
if (cpath == NULL)
goto cont;
cname = canonicalName(combine(cpath, name));
//printf("FileName::safeSearchPath(): cname=%s\n", cname);
if (cname == NULL)
goto cont;
//printf("FileName::safeSearchPath(): exists=%i "
// "strncmp(cpath, cname, %i)=%i\n", exists(cname),
// strlen(cpath), strncmp(cpath, cname, strlen(cpath)));
// exists and name is *really* a "child" of path
if (exists(cname) && strncmp(cpath, cname, strlen(cpath)) == 0)
{
free(cpath);
char *p = mem.strdup(cname);
free(cname);
return p;
}
cont:
if (cpath)
free(cpath);
if (cname)
free(cname);
}
}
return NULL;
#else
assert(0);
#endif
}
int FileName::exists(const char *name)
{
#if POSIX
@@ -886,6 +972,52 @@ void FileName::ensurePathExists(const char *path)
}
}
/******************************************
* Return canonical version of name in a malloc'd buffer.
* This code is high risk.
*/
char *FileName::canonicalName(const char *name)
{
#if linux
// Lovely glibc extension to do it for us
return canonicalize_file_name(name);
#elif POSIX
#if _POSIX_VERSION >= 200809L || defined (linux)
// NULL destination buffer is allowed and preferred
return realpath(name, NULL);
#else
char *cname = NULL;
#if PATH_MAX
/* PATH_MAX must be defined as a constant in <limits.h>,
* otherwise using it is unsafe due to TOCTOU
*/
size_t path_max = (size_t)PATH_MAX;
if (path_max > 0)
{
/* Need to add one to PATH_MAX because of realpath() buffer overflow bug:
* http://isec.pl/vulnerabilities/isec-0011-wu-ftpd.txt
*/
cname = (char *)malloc(path_max + 1);
if (cname == NULL)
return NULL;
}
#endif
return realpath(name, cname);
#endif
#elif _WIN32
/* Apparently, there is no good way to do this on Windows.
* GetFullPathName isn't it.
*/
assert(0);
return NULL;
#else
assert(0);
return NULL;
#endif
}
/****************************** File ********************************/
File::File(FileName *n)

View File

@@ -1,9 +1,9 @@
// Copyright (c) 1999-2006 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// www.digitalmars.com
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.
@@ -156,8 +156,10 @@ struct FileName : String
void CopyTo(FileName *to);
static char *searchPath(Array *path, const char *name, int cwd);
static char *safeSearchPath(Array *path, const char *name);
static int exists(const char *name);
static void ensurePathExists(const char *path);
static char *canonicalName(const char *name);
};
struct File : Object

View File

@@ -1,5 +1,5 @@
// Copyright (c) 1999-2005 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
@@ -11,6 +11,7 @@
#include <assert.h>
#include "root.h"
#include "speller.h"
#include "mars.h"
#include "init.h"
@@ -361,3 +362,27 @@ void Scope::setNoFree()
//assert(0);
}
}
/************************************************
* Given the failed search attempt, try to find
* one with a close spelling.
*/
void *scope_search_fp(void *arg, const char *seed)
{
//printf("scope_search_fp('%s')\n", seed);
Scope *sc = (Scope *)arg;
Identifier id(seed, 0);
Module::clearCache();
Dsymbol *s = sc->search(0, &id, NULL);
return s;
}
Dsymbol *Scope::search_correct(Identifier *ident)
{
if (global.gag)
return NULL; // don't do it for speculative compiles; too time consuming
return (Dsymbol *)speller(ident->toChars(), &scope_search_fp, this, idchars);
}

View File

@@ -116,6 +116,7 @@ struct Scope
void mergeCallSuper(Loc loc, unsigned cs);
Dsymbol *search(Loc loc, Identifier *ident, Dsymbol **pscopesym);
Dsymbol *search_correct(Identifier *ident);
Dsymbol *insert(Dsymbol *s);
ClassDeclaration *getClassScope();

View File

@@ -1,6 +1,6 @@
// Compiler implementation of the D programming language
// Copyright (c) 1999-2009 by Digital Mars
// Copyright (c) 1999-2010 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
@@ -101,13 +101,10 @@ void Statement::error(const char *format, ...)
void Statement::warning(const char *format, ...)
{
if (global.params.warnings && !global.gag)
{
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
va_list ap;
va_start(ap, format);
::vwarning(loc, format, ap);
va_end( ap );
}
int Statement::hasBreak()
@@ -737,6 +734,7 @@ Statement *UnrolledLoopStatement::semantic(Scope *sc)
Statement *s = (Statement *) statements->data[i];
if (s)
{
//printf("[%d]: %s\n", i, s->toChars());
s = s->semantic(scd);
statements->data[i] = s;
}
@@ -798,6 +796,7 @@ int UnrolledLoopStatement::blockExit()
return result;
}
int UnrolledLoopStatement::comeFrom()
{ int comefrom = FALSE;
@@ -3466,7 +3465,6 @@ Statement *ReturnStatement::semantic(Scope *sc)
Statement *s = new ExpStatement(loc, exp);
exp = NULL;
s = s->semantic(sc);
loc = 0;
return new CompoundStatement(loc, s, this);
}

View File

@@ -3701,7 +3701,12 @@ TemplateDeclaration *TemplateInstance::findTemplateDeclaration(Scope *sc)
id = name;
s = sc->search(loc, id, &scopesym);
if (!s)
{ error("template '%s' is not defined", id->toChars());
{
s = sc->search_correct(id);
if (s)
error("template '%s' is not defined, did you mean %s?", id->toChars(), s->toChars());
else
error("template '%s' is not defined", id->toChars());
return NULL;
}
#if LOG