private static TypeSymbol NarrowTypeInternal(ITypeManager typeManager, SyntaxBase expression, TypeSymbol targetType, IDiagnosticWriter diagnosticWriter, TypeMismatchErrorFactory typeMismatchErrorFactory, bool skipConstantCheck, bool skipTypeErrors) { if (targetType is ResourceType targetResourceType) { // When assigning a resource, we're really assigning the value of the resource body. var narrowedBody = NarrowTypeInternal(typeManager, expression, targetResourceType.Body.Type, diagnosticWriter, typeMismatchErrorFactory, skipConstantCheck, skipTypeErrors); return(new ResourceType(targetResourceType.TypeReference, narrowedBody)); } if (targetType is ModuleType targetModuleType) { var narrowedBody = NarrowTypeInternal(typeManager, expression, targetModuleType.Body.Type, diagnosticWriter, typeMismatchErrorFactory, skipConstantCheck, skipTypeErrors); return(new ModuleType(targetModuleType.Name, narrowedBody)); } // TODO: The type of this expression and all subexpressions should be cached var expressionType = typeManager.GetTypeInfo(expression); // since we dynamically checked type, we need to collect the errors but only if the caller wants them if (skipTypeErrors == false && expressionType is ErrorType) { diagnosticWriter.WriteMultiple(expressionType.GetDiagnostics()); return(targetType); } // basic assignability check if (AreTypesAssignable(expressionType, targetType) == false) { // fundamentally different types - cannot assign diagnosticWriter.Write(typeMismatchErrorFactory(targetType, expressionType, expression)); return(targetType); } // object assignability check if (expression is ObjectSyntax objectValue && targetType is ObjectType targetObjectType) { return(NarrowObjectType(typeManager, objectValue, targetObjectType, diagnosticWriter, skipConstantCheck)); } if (expression is ObjectSyntax objectDiscriminated && targetType is DiscriminatedObjectType targetDiscriminated) { return(NarrowDiscriminatedObjectType(typeManager, objectDiscriminated, targetDiscriminated, diagnosticWriter, skipConstantCheck)); } // array assignability check if (expression is ArraySyntax arrayValue && targetType is ArrayType targetArrayType) { return(NarrowArrayAssignmentType(typeManager, arrayValue, targetArrayType, diagnosticWriter, skipConstantCheck)); } if (targetType is UnionType targetUnionType) { return(UnionType.Create(targetUnionType.Members.Where(x => AreTypesAssignable(expressionType, x.Type) == true))); } return(targetType); }