TEST(TestEnumeration, CaseAccess_SwitchCase) { SEMANTIC_ANALYZE(L"enum CompassPoint { \n" L"case North\n" L"case South\n" L"case East\n" L"case West\n" L"}\n" L"var directionToHead : CompassPoint = .North;\n" L"directionToHead = .North\n" L"switch directionToHead {\n" L"case .North:\n" L" break\n" L"case .South:\n" L" break\n" L"case .East:\n" L" break\n" L"case .West:\n" L" break\n" L"}"); dumpCompilerResults(compilerResults); ASSERT_EQ(0, compilerResults.numResults()); SymbolPtr directionToHead; TypePtr CompassPoint; ASSERT_NOT_NULL(directionToHead = scope->lookup(L"directionToHead")); ASSERT_NOT_NULL(CompassPoint = dynamic_pointer_cast<Type>(scope->lookup(L"CompassPoint"))); ASSERT_EQ(CompassPoint, directionToHead->getType()); }
TEST(TestFunctionOverloads, testFunc) { SEMANTIC_ANALYZE(L"func test() -> String {}\n" L"let a = test()"); dumpCompilerResults(compilerResults); SymbolPtr a; ASSERT_NOT_NULL(a = scope->lookup(L"a")); TypePtr type = a->getType(); ASSERT_NOT_NULL(type); TypePtr t_String = symbolRegistry.lookupType(L"String"); ASSERT_TRUE(type == t_String); }
TEST(TestEnumeration, ImplicitRawRepresentable) { SEMANTIC_ANALYZE(L"enum Planet: Int {\n" L" case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune\n" L"}\n" L"let earthsOrder = Planet.Earth.rawValue"); dumpCompilerResults(compilerResults); ASSERT_EQ(0, compilerResults.numResults()); SymbolPtr earthsOrder; ASSERT_NOT_NULL(earthsOrder = scope->lookup(L"earthsOrder")); ASSERT_EQ(symbolRegistry.getGlobalScope()->Int(), earthsOrder->getType()); }
TEST(TestFunctionOverloads, CallVariable) { SEMANTIC_ANALYZE(L"var a = {(a : Int) -> Bool in return true}\n" L"var b = a(3)"); dumpCompilerResults(compilerResults); ASSERT_EQ(0, compilerResults.numResults()); SymbolPtr b; ASSERT_NOT_NULL(b = scope->lookup(L"b")); TypePtr type = b->getType(); ASSERT_NOT_NULL(type); TypePtr t_Bool = symbolRegistry.lookupType(L"Bool"); ASSERT_TRUE(type == t_Bool); }
TEST(TestFunctionOverloads, testClosureLiteral) { SEMANTIC_ANALYZE(L"let a = {(c : Int, b : Int)->Int in return c + b}(1, 2)"); dumpCompilerResults(compilerResults); ASSERT_EQ(0, compilerResults.numResults()); SymbolPtr a; ASSERT_NOT_NULL(a = scope->lookup(L"a")); TypePtr type = a->getType(); ASSERT_NOT_NULL(type); TypePtr t_Int = symbolRegistry.lookupType(L"Int"); ASSERT_TRUE(type == t_Int); }
TEST(TestEnumeration, CaseAccess) { SEMANTIC_ANALYZE(L"enum CompassPoint { \n" L"case North\n" L"case South\n" L"case East\n" L"case West\n" L"}\n" L"var a = CompassPoint.East;"); dumpCompilerResults(compilerResults); ASSERT_EQ(0, compilerResults.numResults()); SymbolPtr a; TypePtr CompassPoint; ASSERT_NOT_NULL(a = scope->lookup(L"a")); ASSERT_NOT_NULL(CompassPoint = dynamic_pointer_cast<Type>(scope->lookup(L"CompassPoint"))); ASSERT_EQ(CompassPoint, a->getType()); }
bool DeclarationAnalyzer::verifyProtocolConform(const TypePtr& type, const TypePtr& protocol, bool supressError) { for(auto entry : protocol->getDeclaredMembers()) { SymbolPtr requirement = entry.second; if(FunctionOverloadedSymbolPtr funcs = std::dynamic_pointer_cast<FunctionOverloadedSymbol>(requirement)) { //verify function for(auto func : *funcs) { bool success = verifyProtocolFunction(type, protocol, func, supressError); if(!success) return false; } } else if(FunctionSymbolPtr func = std::dynamic_pointer_cast<FunctionSymbol>(requirement)) { //verify function bool success = verifyProtocolFunction(type, protocol, func, supressError); if(!success) return false; } /* else if(requirement == Type::getPlaceHolder()) { //verify inner type SymbolPtr sym = type->getAssociatedType(entry.first); if(!(std::dynamic_pointer_cast<Type>(sym))) { //Type %0 does not conform to protocol %1, unimplemented type %2 error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_UNIMPLEMENTED_TYPE_3, type->getName(), protocol->getName(), entry.first); } }*/ else if(TypePtr t = std::dynamic_pointer_cast<Type>(requirement)) { //if type doesn't declare a type alias but the protocol has defined it with a full definition, then we will implicitly declare it wstring name = t->getName(); TypePtr originalType = t->resolveAlias(); if(type->getAssociatedType(name) == nullptr && originalType != nullptr && originalType->getCategory() != Type::Alias) { TypeBuilderPtr builder = static_pointer_cast<TypeBuilder>(type); builder->addMember(name, originalType); } //typealias checking is moved to second stage, when all other members verified continue; } else if(dynamic_pointer_cast<ComputedPropertySymbol>(requirement) || dynamic_pointer_cast<SymbolPlaceHolder>(requirement)) { //verify computed properties SymbolPtr sym = type->getMember(entry.first); //ComputedPropertySymbolPtr sp = std::dynamic_pointer_cast<ComputedPropertySymbol>(sym); if(!sym || !checkTypeConform(type, requirement->getType(), sym->getType())) { if(!supressError) { error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_UNIMPLEMENTED_PROPERTY_3, type->getName(), protocol->getName(), entry.first); } return false; } bool expectedSetter = requirement->hasFlags(SymbolFlagWritable); bool actualSetter = sym->hasFlags(SymbolFlagWritable); if(expectedSetter && !actualSetter) { if(!supressError) { error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_UNWRITABLE_PROPERTY_3, type->getName(), protocol->getName(), entry.first); } return false; } } } //check again for associated types for(auto entry : protocol->getAssociatedTypes()) { if(entry.second->resolveAlias() != nullptr) continue; TypePtr t = type->getAssociatedType(entry.first); if(t == nullptr) { //undefined type alias if(!supressError) { error(type->getReference(), Errors::E_TYPE_DOES_NOT_CONFORM_TO_PROTOCOL_2_, type->getName(), protocol->getName()); } return false; } } return true; }
void SemanticAnalyzer::visitValueBinding(const ValueBindingPtr& node) { PatternPtr name = node->getName(); //tuple was already exploded in declaration analyzer if(name->getNodeType() == NodeType::Tuple) { validateTupleTypeDeclaration(node); } if (name->getNodeType() != NodeType::Identifier) return; if(node->getOwner()->isReadOnly() && !node->getInitializer() && ctx->currentType == nullptr) { error(node, Errors::E_LET_REQUIRES_INITIALIZER); return; } //handle type inference for temporary variable if(node->isTemporary() && node->getInitializer()) { //temporary variable always has an initializer TypePtr initializerType; TypePtr declaredType = node->getType(); SCOPED_SET(ctx->contextualType, declaredType); node->getInitializer()->accept(this); initializerType = node->getInitializer()->getType(); assert(initializerType != nullptr); if(declaredType) { //it has both type definition and initializer, then we need to check if the initializer expression matches the type annotation if(!initializerType->canAssignTo(declaredType)) { error(node, Errors::E_CANNOT_CONVERT_EXPRESSION_TYPE_2, initializerType->toString(), declaredType->toString()); return; } } else { node->setType(initializerType); } } //add implicitly constructor for Optional IdentifierPtr id = static_pointer_cast<Identifier>(node->getName()); SymbolScope* currentScope = symbolRegistry->getCurrentScope(); TypePtr declaredType = node->getType() ? node->getType() : lookupType(node->getDeclaredType()); SCOPED_SET(ctx->contextualType, declaredType); if(!declaredType && !node->getInitializer()) { error(node, Errors::E_TYPE_ANNOTATION_MISSING_IN_PATTERN); return; } SymbolPtr sym = currentScope->lookup(id->getIdentifier()); assert(sym != nullptr); SymbolPlaceHolderPtr placeholder = std::dynamic_pointer_cast<SymbolPlaceHolder>(sym); assert(placeholder != nullptr); if(declaredType) { placeholder->setType(declaredType); } ExpressionPtr initializer = node->getInitializer(); if(initializer) { placeholder->setFlags(SymbolFlagInitializing, true); ExpressionPtr initializer = transformExpression(declaredType, node->getInitializer()); node->setInitializer(initializer); TypePtr actualType = initializer->getType(); assert(actualType != nullptr); if(declaredType) { if(!Type::equals(actualType, declaredType) && !canConvertTo(initializer, declaredType)) { error(initializer, Errors::E_CANNOT_CONVERT_EXPRESSION_TYPE_2, actualType->toString(), declaredType->toString()); return; } } if(!declaredType) placeholder->setType(actualType); } assert(placeholder->getType() != nullptr); placeholder->setFlags(SymbolFlagInitializing, false); //optional type always considered initialized, compiler will make it has a default value nil TypePtr symbolType = sym->getType(); GlobalScope* global = symbolRegistry->getGlobalScope(); if(initializer || global->isOptional(symbolType) || global->isImplicitlyUnwrappedOptional(symbolType)) markInitialized(placeholder); if (initializer) placeholder->setFlags(SymbolFlagHasInitializer, true); if(node->isTemporary()) { placeholder->setFlags(SymbolFlagTemporary, true); markInitialized(placeholder); } if(!node->isTemporary()) { //check access control level DeclarationType decl = ctx->currentType ? DeclarationTypeProperty : DeclarationTypeVariable; declarationAnalyzer->verifyAccessLevel(node->getOwner(), placeholder->getType(), decl, ComponentTypeType); } if(ctx->currentType && ctx->currentType->getCategory() == Type::Protocol) { error(node, Errors::E_PROTOCOL_VAR_MUST_BE_COMPUTED_PROPERTY_1); } }