blob: 731964fc78221ae73118981252232503bcfabc30 [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
// This only resolves names. After this phase runs, all names will be resolved. This means that all
// XYZRef objects now know who they resolve to. This does not yet allow type analysis, because there
// are still TypeDefs. This throws type errors for failed name resolutions and for things that are
// more convenient to check here than in the Checker. This usually involves things that need to be
// checked before TypeRefToTypeDefSkipper.
class NameResolver extends Visitor {
constructor(nameContext)
{
super();
this._nameContext = nameContext;
}
doStatement(statement)
{
this._nameContext.doStatement(statement, () => statement.visit(this));
}
_visitTypeParametersAndBuildNameContext(node)
{
let nameContext = new NameContext(this._nameContext);
for (let typeParameter of node.typeParameters) {
nameContext.add(typeParameter);
typeParameter.visit(this);
}
return nameContext;
}
visitFunc(node)
{
let checker = new NameResolver(this._visitTypeParametersAndBuildNameContext(node));
node.returnType.visit(checker);
for (let parameter of node.parameters)
parameter.visit(checker);
}
visitFuncDef(node)
{
let contextWithTypeParameters = this._visitTypeParametersAndBuildNameContext(node);
let checkerWithTypeParameters = new NameResolver(contextWithTypeParameters);
node.returnType.visit(checkerWithTypeParameters);
let contextWithParameters = new NameContext(contextWithTypeParameters);
for (let parameter of node.parameters) {
parameter.visit(checkerWithTypeParameters);
contextWithParameters.add(parameter);
}
let checkerWithParameters = new NameResolver(contextWithParameters);
node.body.visit(checkerWithParameters);
}
visitBlock(node)
{
let checker = new NameResolver(new NameContext(this._nameContext));
for (let statement of node.statements)
statement.visit(checker);
}
visitIfStatement(node)
{
node.conditional.visit(this);
// The bodies might not be Blocks, so we need to explicitly give them a new context.
node.body.visit(new NameResolver(new NameContext(this._nameContext)));
if (node.elseBody)
node.elseBody.visit(new NameResolver(new NameContext(this._nameContext)));
}
visitWhileLoop(node)
{
node.conditional.visit(this);
// The bodies might not be Blocks, so we need to explicitly give them a new context.
node.body.visit(new NameResolver(new NameContext(this._nameContext)));
}
visitDoWhileLoop(node)
{
// The bodies might not be Blocks, so we need to explicitly give them a new context.
node.body.visit(new NameResolver(new NameContext(this._nameContext)));
node.conditional.visit(this);
}
visitForLoop(node)
{
let newResolver = new NameResolver(new NameContext(this._nameContext))
if (node.initialization)
node.initialization.visit(newResolver);
if (node.condition)
node.condition.visit(newResolver);
if (node.increment)
node.increment.visit(newResolver);
node.body.visit(newResolver);
}
visitProtocolDecl(node)
{
for (let parent of node.extends)
parent.visit(this);
let nameContext = new NameContext(this._nameContext);
nameContext.add(node.typeVariable);
let checker = new NameResolver(nameContext);
for (let signature of node.signatures)
signature.visit(checker);
}
visitProtocolRef(node)
{
let result = this._nameContext.get(Protocol, node.name);
if (!result)
throw new WTypeError(node.origin.originString, "Could not find protocol named " + node.name);
node.protocolDecl = result;
}
visitProtocolFuncDecl(node)
{
this.visitFunc(node);
let funcs = this._nameContext.get(Func, node.name);
if (!funcs)
throw new WTypeError(node.origin.originString, "Cannot find any functions named " + node.name);
node.possibleOverloads = funcs;
}
visitTypeDef(node)
{
let nameContext = new NameContext(this._nameContext);
for (let typeParameter of node.typeParameters) {
typeParameter.visit(this);
nameContext.add(typeParameter);
}
let checker = new NameResolver(nameContext);
node.type.visit(checker);
}
visitStructType(node)
{
let nameContext = new NameContext(this._nameContext);
for (let typeParameter of node.typeParameters) {
typeParameter.visit(this);
nameContext.add(typeParameter);
}
let checker = new NameResolver(nameContext);
for (let field of node.fields)
field.visit(checker);
}
_resolveTypeArguments(typeArguments)
{
for (let i = 0; i < typeArguments.length; ++i) {
let typeArgument = typeArguments[i];
if (typeArgument instanceof TypeOrVariableRef) {
let thing = this._nameContext.get(Anything, typeArgument.name);
if (!thing)
new WTypeError(typeArgument.origin.originString, "Could not find type or variable named " + typeArgument.name);
if (thing instanceof Value)
typeArguments[i] = new VariableRef(typeArgument.origin, typeArgument.name);
else if (thing instanceof Type)
typeArguments[i] = new TypeRef(typeArgument.origin, typeArgument.name, []);
else
throw new WTypeError(typeArgument.origin.originString, "Type argument resolved to wrong kind of thing: " + thing.kind);
}
if (typeArgument[i] instanceof Value
&& !typeArgument[i].isConstexpr)
throw new WTypeError(typeArgument[i].origin.originString, "Expected constexpr");
}
}
visitTypeRef(node)
{
this._resolveTypeArguments(node.typeArguments);
let type = node.type;
if (!type) {
type = this._nameContext.get(Type, node.name);
if (!type)
throw new WTypeError(node.origin.originString, "Could not find type named " + node.name);
node.type = type;
}
if (type.typeParameters.length != node.typeArguments.length)
throw new WTypeError(node.origin.originString, "Wrong number of type arguments (passed " + node.typeArguments.length + ", expected " + type.typeParameters.length + ")");
for (let i = 0; i < type.typeParameters.length; ++i) {
let parameterIsType = type.typeParameters[i] instanceof TypeVariable;
let argumentIsType = node.typeArguments[i] instanceof Type;
node.typeArguments[i].visit(this);
if (parameterIsType && !argumentIsType)
throw new WTypeError(node.origin.originString, "Expected type, but got value at argument #" + i);
if (!parameterIsType && argumentIsType)
throw new WTypeError(node.origin.originString, "Expected value, but got type at argument #" + i);
}
}
visitReferenceType(node)
{
let nameContext = new NameContext(this._nameContext);
node.elementType.visit(new NameResolver(nameContext));
}
visitVariableDecl(node)
{
this._nameContext.add(node);
node.type.visit(this);
if (node.initializer)
node.initializer.visit(this);
}
visitVariableRef(node)
{
if (node.variable)
return;
let result = this._nameContext.get(Value, node.name);
if (!result)
throw new WTypeError(node.origin.originString, "Could not find variable named " + node.name);
node.variable = result;
}
visitReturn(node)
{
node.func = this._nameContext.currentStatement;
super.visitReturn(node);
}
_handlePropertyAccess(node)
{
node.possibleGetOverloads = this._nameContext.get(Func, node.getFuncName);
node.possibleSetOverloads = this._nameContext.get(Func, node.setFuncName);
node.possibleAndOverloads = this._nameContext.get(Func, node.andFuncName);
if (!node.possibleGetOverloads && !node.possibleAndOverloads)
throw new WTypeError(node.origin.originString, "Cannot find either " + node.getFuncName + " or " + node.andFuncName);
}
visitDotExpression(node)
{
// This could be a reference to an enum. Let's resolve that now.
if (node.struct instanceof VariableRef) {
let enumType = this._nameContext.get(Type, node.struct.name);
if (enumType && enumType instanceof EnumType) {
let enumMember = enumType.memberByName(node.fieldName);
if (!enumMember)
throw new WTypeError(node.origin.originString, "Enum " + enumType.name + " does not have a member named " + node.fieldName);
node.become(new EnumLiteral(node.origin, enumMember));
return;
}
}
this._handlePropertyAccess(node);
super.visitDotExpression(node);
}
visitIndexExpression(node)
{
this._handlePropertyAccess(node);
super.visitIndexExpression(node);
}
visitCallExpression(node)
{
this._resolveTypeArguments(node.typeArguments);
let funcs = this._nameContext.get(Func, node.name);
if (funcs)
node.possibleOverloads = funcs;
else {
let type = this._nameContext.get(Type, node.name);
if (!type)
throw new WTypeError(node.origin.originString, "Cannot find any function or type named \"" + node.name + "\"");
node.becomeCast(type);
node.possibleOverloads = this._nameContext.get(Func, "operator cast");
if (!node.possibleOverloads)
throw new WTypeError(node.origin.originString, "Cannot find any operator cast implementations in cast to " + type);
}
super.visitCallExpression(node);
}
}